Nginx least_conn 代理 gRPC 时需主动健康检查、合理 keepalive、gRPC 专用超时及客户端 round_robin 策略协同,否则连接数统计失真,假死节点持续吸流引发雪崩。

排查 Nginx least_conn 代理 gRPC 服务异常,不能只看“连接数最少”这个结果,而要验证它是否真实反映了后端的可用性与负载状态。gRPC 是长连接、多路复用的 HTTP/2 流式协议,least_conn 若未与之对齐,极易把请求持续打向已卡死但 TCP 连接未断的节点,造成隐蔽雪崩。
确认 least_conn 是否真正生效
该策略默认仅在 upstream 健康节点中比较活跃连接数。若后端假死(进程仍在、端口通、但不处理 stream),Nginx 仍会把它当“健康”计入选择池——此时 least_conn 反而加剧问题。
- 检查 error_log 中是否有
upstream timed out或upstream prematurely closed connection,这类日志说明后端响应异常,但未被剔除 - 用
ss -tn state established | grep :后端端口 | wc -l在各后端服务器上对比 ESTABLISHED 连接数;若某台远高于其他且持续不降,大概率已堆积阻塞 - 启用自定义日志,添加
$upstream_addr $upstream_connect_time $upstream_header_time,观察是否大量请求落在同一地址且 connect_time 突增
必须补全健康检查机制
gRPC 场景下,被动失败检测(如 proxy_next_upstream error timeout)严重滞后。一个卡住的 gRPC server 可能维持 TCP 连接数周,least_conn 会持续分发新 stream 给它。
- 优先启用主动健康检查:
health_check interval=3 fails=2 passes=2 match=ok;(需 Nginx Plus 或编译ngx_http_upstream_check_module) - 若用开源版,至少配强被动策略:
proxy_next_upstream error timeout http_502 http_503 http_504 non_idempotent;并为每个server显式设max_fails=1 fail_timeout=10s - 健康检查路径必须独立(如
/health),返回轻量 JSON,不走业务逻辑、不依赖数据库或缓存
验证 keepalive 与连接统计是否真实
least_conn 统计的是 Nginx 与后端之间的活跃 TCP 连接数,包括空闲的 HTTP/2 keepalive 连接。若配置不当,这个数字就失去参考价值。
- 检查 upstream 是否设置了
keepalive 512;(不宜低于 256,也不宜超过后端连接池上限) - 确认
keepalive_timeout 75s;已显式声明(默认继承 proxy_timeout,常为 60s,易误杀活跃流) - 后端必须支持 H2C 或 TLS,并开启长连接:如 Go gRPC server 默认支持 h2c;Java 需显式配置
NettyServerBuilder.usePlaintext() - 禁用
grpc_buffering on;——缓冲会破坏流帧实时性,导致连接看似空闲实则卡在 buffer 中
聚焦 gRPC 特有超时与流行为
HTTP/1.x 的 proxy_read_timeout 对 gRPC 无效。least_conn 分配后,若单个 stream 因超时被中断,连接不会自动释放,后续 stream 仍可能复用该“半死”连接。
- 必须使用
grpc_read_timeout 3600;和grpc_send_timeout 3600;(单位秒),按流级而非请求级控制 - 设
reset_timedout_connection off;,避免 Nginx 主动 RST 中断还在传输的 stream - 客户端必须启用
round_robin负载策略(非pick_first),否则所有 stream 都打向第一个解析出的 IP,least_conn完全失效


















