NGINX 代理 gRPC 请求

陪她去流浪 桃子 2020年04月18日 编辑 阅读次数:5854

我的博客服务器开放了两个端口,一个用于服务 HTTP 请求,一个用于服务客户端的 gRPC 请求。

后来对 gRPC 了解更深入之后,发现其底层的传输层协议其实是基于 HTTP/2 的。同时,看到了 NGINX 官方的 gRPC 支持。 于是想用 NGINX 代理 gRPC 请求,这样就不用再考虑 gRPC 的安全问题了(但 HTTP/2 并非一定是 HTTPS 的)。

安全问题是其一,最大的好处,我觉得是:

不用在配置文件中区别对待 HTTP 和 gRPC 地址了

检测 NGINX 是否支持

NGINX 从版本 v1.13.10 开始支持 gRPC 代理系列指令(参考:nginx_http_grpc_module),并且开始了ngx_http_v2_module模块。

可以以下方式查看:

1
2
3
4
5
$ nginx -vV
nginx version: nginx/1.17.8
...
--with-http_v2_module
...

可以看到我的版本高于 v1.13.10 并且有 http_v2_module

NGINX 的简单配置

然后就可以像下面这样一句话实现代理 gRPC 请求:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
server {
	# 省略其它配置

	# 必须是 HTTP2
	listen 82 http2;

	# 连接自动断开的超时设置
	grpc_read_timeout 600s;

	# 必须设置,否则可能因 body 太大导致 pb 协议错误。
	# 理论上 grpc 协议不需要限制大小,按需。
	client_max_body_size 0

	location / {
		grpc_pass grpc://localhost:2563;
	}
}

grpc_passproxy_pass 的用法非常类似。 其中,grpc:// 是可选的,如果 gRPC 是安全连接,则应该用grpcs://

值得注意的是,GRPC 是基于 HTTP/2 的,所以 nginx 在配置的时候也必须配置 HTTP/2 的支持:

1
2
3
server {
	listen 80 http2;
}

或者,如果是 HTTPS 服务器的话:

1
2
3
4
5
server {
	listen 443 ssl http2;

	# 证书配置
}

gRPC 客户端的配置

然后修改你的 gRPC 客户端的代码,把原来的 gRPC 地址改成 NGINX 的地址即可。 注意::gRPC 的地址是没有协议头的,即应去掉 NGINX 地址中的 http://https://,但是端口却是必填的。比如:

  • 你的 nginx 地址是 http://example.com,则 gRPC 地址:example.com:80
  • 你的 nginx 地址是 https://example.com,则 gRPC 地址:example.com:443
  • 你的 nginx 地址是 http://example.com:8080,则 gRPC 地址:example.com:8080

对于 gRPC 客户端,如果 nginx 是不安全连接(非 HTTPS),则应该使用类似下面的代码:

1
2
3
4
conn, err := grpc.Dial(
	endpoint,
	grpc.WithInsecure(),
)

而如果是安全的(使用了有效的 HTTPS 证书):

1
2
3
4
conn, err := grpc.Dial(
	endpoint,
	grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
)

而如果是使用了自签(或过期等)证书,并且想忽略安全检测:

1
2
3
4
5
6
conn, err := grpc.Dial(
	endpoint,
	grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
		InsecureSkipVerify: true,
	})),
)

也可以客户端自己添加服务端证书以校验,本文从略,有需要再添加。(考虑到应该少有 nginx 服务器使用不安全的证书)

NGINX 根据 gRPC 方法路由代理

首先,需要知道你的 Protocol Buffer 包的方法全称

打开你的 PB 生成的RPC 服务对应的*.pb.go文件,比如:service.pb.go。 然后搜索 Invoke 或者 FullMethod,你应该会看到类似/protocols.TaoBlog/ListComments的字符串,这些就是方法全称。 双斜杠//内的是包名.服务名,然后是方法名

如果要根据包名.服务名来路由,则可以像下面这样:

1
2
3
4
5
6
7
location /protocols.TaoBlog/ {
	grpc_pass grpc://192.168.20.11:50051;
}

location /helloworld.Greeter/ {
	grpc_pass grpc://192.168.20.11:50052;
}

NGINX 中 gRPC 服务器的负载均衡

gRPC 微服务很多,负载均衡无可避免。

使用 NGINX 自带的 upstream 即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
upstream grpcservers {
	server 192.168.20.11:50051;
	server 192.168.20.12:50051;
}

server {
	# 省略其它常用配置

	location /helloworld.Greeter/ {
		grpc_pass grpc://grpcservers;
	}
}

其它

我只使用了nginx_http_grpc_module模块的一条指令完成了 NGINX 对 gRPC 的代理。 如果需要更多配置,请参阅 NGINX 相关文档。

参考

标签:代理 · nginx · gRPC