发送grpc-go/HTTP2请求时nginx报错recv() failed偶现问题如何解决?
gRPC请求偶发Nginx 104报错排查修复方案
2021/10/01 17:06:08 [info] 18799#18799: *9 recv() failed (104: Connection reset by peer) while processing HTTP/2 connection, client: 127.0.0.1, server: 0.0.0.0:50050
该报错是Nginx代理gRPC请求场景下的常见偶现问题,核心原因是两端连接生命周期配置不匹配或者底层连接异常被重置,具体排查修复步骤如下:
常见触发原因
- 后端gRPC Go服务主动断开了空闲过久的连接,而Nginx仍在复用该连接转发新请求
- Nginx的HTTP/2连接超时配置和后端gRPC服务的连接生命周期配置不一致
- 报错时间点并发请求突增,后端gRPC服务的连接队列被打满,直接重置了新建连接
- gRPC客户端连接复用逻辑异常,将请求发送到了已经被服务端标记为关闭的连接上
- 内核TCP参数配置不合理,如syn队列过小、timewait连接回收策略异常
排查步骤
- 优先确认Nginx版本:1.19.x之前的稳定版本对gRPC HTTP/2连接复用的支持存在已知bug,版本问题是高频诱因
- 对比两端连接超时配置:
- Nginx侧检查
grpc_connect_timeout、grpc_send_timeout、grpc_read_timeout、keepalive_timeout、http2_idle_timeout的配置值 - gRPC Go服务侧检查
grpc.KeepaliveParams中MaxConnectionIdle、MaxConnectionAge的设置值
- Nginx侧检查
- 抓包定位RST来源:在Nginx和gRPC服务端同时用
tcpdump抓取50050端口的流量,确认RST包是哪一侧发出,以及发RST前是否有连接空闲超时、服务端返回GOAWAY帧的特征 - 检查服务端运行状态:确认报错时间点gRPC服务是否存在CPU、内存打满,或者连接数、QPS突增的异常情况
修复方案
核心修复逻辑是保证Nginx的空闲超时时间永远小于后端服务的主动断开时间,避免Nginx复用已经失效的连接
- 配置对齐两端超时规则,示例配置如下:
Nginx侧配置:
gRPC Go服务侧配置:http { http2_idle_timeout 300s; # 小于gRPC服务的MaxConnectionIdle配置 keepalive_timeout 300s; server { listen 50050 http2; location / { grpc_pass grpc://your_grpc_backend; grpc_read_timeout 600s; grpc_send_timeout 600s; grpc_next_upstream_tries 3; grpc_set_header Connection ""; # 开启长连接复用 } } }import "google.golang.org/grpc/keepalive" s := grpc.NewServer( grpc.KeepaliveParams(keepalive.ServerParameters{ MaxConnectionIdle: 350 * time.Second, // 大于Nginx的http2_idle_timeout MaxConnectionAge: 7200 * time.Second, Time: 10 * time.Second, Timeout: 20 * time.Second, }), ) - 升级Nginx到1.20+稳定版本,修复旧版本HTTP/2连接处理的已知问题
- 调整内核TCP参数优化连接处理能力:将
net.core.somaxconn、net.ipv4.tcp_max_syn_backlog调整到1024以上,可根据业务实际并发量进一步调高 - 在gRPC客户端配置重试策略,对
RESET_CONNECTION类型的错误开启自动重试,注意重试前要校验请求的幂等性,避免重复提交
内容的提问来源于stack exchange,提问作者kaushal agrawal
相关产品推荐
相关产品推荐

