grpc-web demo跑起来

最近调研区块链钱包插件的技术可行性,其中牵扯到grpc技术,官方repo有demo,但正常跑起有些门槛儿的,因此这里总结下。

安装

protobuf

1
2
3
4
5
# 推荐安装方式,这样不需要再编译及进行环境变量配置,https://formulae.brew.sh/formula/protobuf
$ brew install protobuf

# 验证安装成功
$ protoc --version

protoc-gen-grpc-web

为了生成PB定义服务的Web端代码需要安装该工具

protoc-gen-grpc-web需要手动安装下,访问https://github.com/grpc/grpc-web/releases

比如我的intel-Mac,下载protoc-gen-grpc-web-1.3.0-darwin-x86https://grpc.io/_64

执行以下命令

1
2
3
4
5
$ sudo mv ~/Downloads/protoc-gen-grpc-web-1.3.0-darwin-x86_64 \
/usr/local/bin/protoc-gen-grpc-web

$ sudo chmod +x /usr/local/bin/protoc-gen-grpc-web

同上,终端输入protoc-gen-grpc-web发现可以识别即表示安装成功。

protoc-gen-grpc-web的安装太麻烦了,定个flag,维护个npm包方便下大家下载安装。

brew使用补充

  1. brew search package可以搜索包有哪些版本源
  2. brew install package@versionNumber 可以直接安装目标版本

测试

按照定义的PB格式文件生成对应的JS版代码。

1
2
$ protoc -I=. helloworld.proto --js_out=import_style=commonjs:. --grpc-web_out=import_style=commonjs,mode=grpcwebtext:.

构建启动

以上步骤完成后,环境层问题即解决。紧接着按照readme的启动步骤一步步执行即可。

https://static.1991421.cn/2022/2022-10-17-172155.jpeg

注意

如果开发机是Mac平台,需要如下两点修改

  • envoy.yaml配置修改

    1
    address: host.docker.internal

    注意,只需要修改这一句即可

  • docker 启动命令调整-去掉network设定

    1
    2
    docker run -d -v "$(pwd)"/envoy.yaml:/etc/envoy/envoy.yaml:ro \
    -p 8080:8080 -p 9901:9901 envoyproxy/envoy:v1.20.0

错误

Http response at 400 or 500 level

启动后,如果遇到该错误,即网络服务不同,查看服务是否正常启动,比如上述代理配置错造成容器通讯问题。如果是envoy代理的某节点挂掉也会导致报错。

常见问题

grpc-web使用中遇到了些问题,官方文档及社区并不活跃,很多坑还得自己踩,这里总结下常见的一些问题

grpc: received message larger than max (1094796627 vs. 4194304)

遇到该报错,存在两种情况

大小设定

  1. grpc.max_send_message_length设置,单位bytes,-1表示没有限制
  2. 服务端/客户端设置相互独立

官方配置见https://grpc.github.io/grpc/core/group__grpc__arg__keys.html#gab4defdabac3610ef8a5946848592458c

–grpc-web_out

确定WEB输出模式是grpcwebtext还是grpcweb,如果是grpcweb,但最终发的是二进制,则会报received message larger than max,有1G之多。

1
$ protoc -I=. helloworld.proto --js_out=import_style=commonjs:. --grpc-web_out=import_style=commonjs,mode=grpcweb:.

413 (Request Entity Too Large)

  1. 确定nginx client_max_body_size配置
  2. 确认grpc-server端grpc.max_receive_message_length配置

自定义头部字段

有时发送请求希望自定义头部,比如这样实现动态代理grpc服务。

实现需要2部分设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var client = new GreeterClient('http://' + window.location.hostname + ':9090', null, null);

// simple unary call
var request = new HelloRequest();
request.setName('World');

client.sayHello(request, {
'X-Grpc-node': 'hello world' // 自定义头部字段
}, (err, response) => {
if (err) {
console.log(`Unexpected error for sayHello: code = ${err.code}` + `, message = "${err.message}"`);
} else {
console.log(response.getMessage());
}
});

nginx设定读取自定义字段从而实现动态代理

1
2
3
4
5
6
7
8
9
10
11
server {
listen 9080;
server_name localhost;
underscores_in_headers on; # 控制自定义头部字段读取,需要开启

location / {
grpc_set_header Content-Type application/grpc;
grpc_pass grpc://$http_x_grpc_node; # 注意http前缀及下划线
}

}

Nginx动态代理gRPC服务

1
2
3
4
5
6
location / {
grpc_set_header Content-Type application/grpc;
grpc_pass grpc://$http_x_grpc_node;

...
}

注意http_x_grpc_node为HTTP自定义头部字段值。如果需要代理SSL服务,对应是grpcs://,具体配置如下

代理gRPCs

1
2
3
4
5
6
7
8
9
10
location / {
grpc_set_header Content-Type application/grpc;
grpc_pass grpcs://$http_x_grpc_node;

# 注意,文件名称中支持变量
grpc_ssl_certificate /var/www/ssl/$http_x_grpc_ssl_cert;
grpc_ssl_certificate_key /var/www/ssl/$http_x_grpc_ssl_cert_key;

...
}

注意

  • grpc_ssl_certificate,grpc_ssl_certificate_key文件值支持变量,因此可以动态代理不同的server。
  • grpc_pass/grpc_ssl_certificate这些指令并不支持在if条件块中书写,因此无法做到grpc和grpcs的动态代理切换。

补充

  1. 在确定RPC server是否正常启动时,可以使用telnet来测试端口联通性,比如telnet 192.168.1.203 16301

参考文档

写在最后

初步尝试下,有以下几点认识

  • gRPC 默认通过 Protocol Buffers 来定义接口。因此grpc使用牵扯到pb
  • gRPC Web是gRPC协议在浏览器端的javascript实现,但想真的连接grpc-server需要代理服务,比如Envoy/Nginx等
  • gRPC Web使Web能间接与后端gRPC服务通信