nginx 怎么代理 gRPC?哪些写法会错、会错成什么样?流式调用会不会被超时误杀?一条长连接上的调用怎么在后端之间分布?消息多大会被网关挡住?这篇用 Go 的 gRPC 服务和 nginx 1.31.6 量出来。
配置 | Unary 结果 | ServerStream 结果 --------------------------+--------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------- (a) http2 on + grpc_pass | OK:A 1 | got=3 code=EOF(正常结束) elapsed_ms=306 (b) 没开 http2 + grpc_pass | ERR:Unavailable:connection_error:_desc_=_"error_reading_server_preface:_http 1 | got=0 code=Unavailable:connection_error:_desc_=_"error_reading_server_preface:_http elapsed_ms=0 (c) http2 on + proxy_pass | ERR:Internal:stream_terminated_by_RST_STREAM_with_error_code:_INTERNAL_ER 1 | got=0 code=Internal:stream_terminated_by_RST_STREAM_with_error_code:_INTERNAL_ER elapsed_ms=1 (d) 直连上游 | OK:A 1 | got=3 code=EOF(正常结束) elapsed_ms=305
要 http2 on 加 grpc_pass;proxy_pass 或没开 http2 都会出错,错误信息各不相同。
调用 | Unary 结果 | ServerStream 结果 -------------------+--------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------- (a) Unary,上游 1.5 s | ERR:Unavailable:unexpected_HTTP_status_code_received_from_server:_504_(Gatew 1 | - (b) 间隔 0.5 s × 4 条 | - | got=4 code=EOF(正常结束) elapsed_ms=2009 (c) 间隔 1.5 s × 2 条 | - | got=0 code=Unavailable:unexpected_HTTP_status_code_received_from_server:_504_(Gatew elapsed_ms=1003 grpc_read_timeout 和 proxy_read_timeout 一样,管的是“两次读到上游数据之间的间隔”:(b) 每次只隔 0.5 s,整个流 2 s 也没有超时;(a) 和 (c) 第一次读就等了 1.5 s,nginx 超时后回 HTTP 504,Go 的 gRPC 库把它翻译成 Unavailable(消息里带着 504)
grpc_read_timeout 管两次读之间的间隔,流式调用只要消息间隔短就不会被误杀;超时表现为 504 → Unavailable。
配置 | 100 个 Unary 的分布(OK:服务名 次数) ----------------------+--------------------------- nginx 轮询 | OK:A 50; OK:B 50 nginx least_conn | OK:A 50; OK:B 50 nginx ip_hash | OK:A 100 client 直连 A(没有 nginx) | OK:A 100 client 到 nginx 只有一条长连接,但 nginx 对每个 gRPC 调用(HTTP/2 的每个流)独立选择上游,所以轮询时请求被平均分到两个后端——不像 client 直连时那样,长连接把所有调用钉在一个后端上(见 gRPC 系列里 client 侧负载均衡的陷阱)
nginx 对每个流独立选上游,一条长连接上的调用也会被分开。
配置 | 0.5 MB | 2 MB | 6 MB ----------------------------+--------+----------------------------------------------------------------------------+--------------------------------------------------------------------------- 默认(client_max_body_size 1m) | OK:A 1 | ERR:Unknown:unexpected_HTTP_status_code_received_from_server:_413_(Reque 1 | ERR:Unknown:unexpected_HTTP_status_code_received_from_server:_413_(Reque 1 client_max_body_size 10m | OK:A 1 | OK:A 1 | OK:A 1 client_max_body_size 0 | OK:A 1 | OK:A 1 | OK:A 1 grpc_pass 的请求体也受 client_max_body_size 限制(默认 1m);超限时 nginx 回 HTTP 413,Go 的 gRPC 库把它翻译成 Unknown(消息是 unexpected HTTP status code received from server: 413),不是 ResourceExhausted 之类一眼看得出原因的错误
client_max_body_size 也限制 gRPC 的消息,超限时 client 看到的是 Unknown。