NGINX gRPC 上游空闲连接被 RST 重置怎么排查

先用 PING 周期和 TCPAbortOnClose 确认连接抖动,再关闭空闲主动 PING 或接受重连,避免套用不安全补丁

说明 NGINX gRPC 上游连接按 keepalive PING 周期被 RST 时,如何用日志、抓包和 TCPAbortOnClose 取证,安全关闭空闲主动 PING 并验证回退。

NGINX gRPC 上游空闲连接被 RST 重置怎么排查

先用 PING 周期和 TCPAbortOnClose 确认连接抖动,再关闭空闲主动 PING 或接受重连,避免套用不安全补丁

直接答案:如果 NGINX 复用 gRPC 上游连接后,连接总是在固定空闲时间被 RST,而业务请求大多仍成功,先核对这个时间是否等于 gRPC 服务端的 keepalive PING 周期。NGINX 上游 Issue #929 在 2026 年 9 月由维护者复现:连接进入通用 upstream keepalive 缓存后,空闲 PING 到达会让 NGINX 为保护缓存正确性而关闭连接。当前安全选择是关闭“没有活跃 RPC 时仍主动 PING”,让 NGINX 的 keepalive_timeout 管理空闲连接;不能直接给 NGINX 打“收到数据就 return”的简单补丁。

本文适用于 NGINX 反向代理 gRPC、upstream 配置了 keepalive,并且 gRPC 服务端允许空闲时主动发送 HTTP/2 PING 的现场。只读日志、抓包和内核计数器检查不会中断业务。修改 PING 策略需要重启或滚动发布 gRPC 服务端,可能造成短时连接重建;先在单个实例灰度,并保留原配置。

先确认是不是同一条故障路径

不要只看一条 connection reset by peer 就下结论。先记录一次 RPC 结束时间、RST 时间、服务端 PING 周期和 NGINX 的 keepalive_timeout。若连接在 2 秒、5 秒或其他固定 PING 周期关闭,而不是在 NGINX 空闲超时点关闭,才与上游复现场景相符。

在 NGINX 主机上只读查看错误日志和连接关闭计数。LOG_FILE 要替换为当前站点错误日志:

LOG_FILE=/var/log/nginx/error.log
grep -E 'keepalive close handler|upstream.*reset|grpc' "$LOG_FILE" | tail -100
nstat -az | grep -E 'TcpExtTCPAbortOnClose|TcpExtTCPAbortOnData'

普通发行版的 NGINX 未必记录 keepalive close handler,因为它需要 debug 日志。没有该行不等于排除;可比较 TcpExtTCPAbortOnClose 在复现前后的增量。这个计数是整台主机口径,其他进程也会影响,因此必须和故障时间、上游地址及抓包一起判断。

抓包时看什么

只在已授权服务器和明确上游端口上短时抓包。UPSTREAM_IP 与 PORT 必须替换;文件可能包含业务元数据,应按内部敏感日志管理:

UPSTREAM_IP=127.0.0.1
PORT=9200
sudo timeout 30 tcpdump -i any -nn -s 256 \
  "host $UPSTREAM_IP and port $PORT" -w /tmp/nginx-grpc-keepalive.pcap

完成后核对:一次 RPC 正常结束、连接进入空闲、服务端按配置发送 PING、随后 NGINX 方向出现 RST。若 RST 发生在活跃 RPC 中,或前面已有上游进程退出、负载均衡摘除、网络超时,就应转去查应用崩溃、发布切换和网络故障,不套用本文结论。

为什么不能直接延长 keepalive_timeout

NGINX 的 upstream keepalive 保存的是每个 worker 的空闲连接,不限制活跃连接总数。keepalive_timeout 只规定空闲连接在缓存中保留多久。Issue #929 的复现中,空闲 PING 比该超时更早到达,所以把 60 秒改成 5 分钟不会解决问题。

同样,不要把 grpc_socket_keepalive on 当成修复。它控制操作系统 TCP keepalive,不负责处理 HTTP/2 PING 帧,与 gRPC 应用层 keepalive 是两套机制。

首选处理:关闭没有活跃 RPC 时的主动 PING

gRPC 官方建议客户端避免无调用时频繁发送 keepalive,并提醒服务端可能用 GOAWAY 限制过于激进的 PING。对于本故障,关键是 gRPC 服务端是否配置了类似 permit_without_calls 的选项。不同语言名称不同,不能照抄某一种 SDK 的字段到另一种语言。

  1. 导出当前 gRPC 服务端 keepalive 配置,记录 PING 间隔、超时和是否允许无调用 PING。
  2. 在一个非关键实例上关闭“无活跃 RPC 仍发送 PING”,其他参数先不变。
  3. 滚动重启该实例,不一次重启全部后端。
  4. 把少量测试流量指向灰度实例,观察至少覆盖多个原 PING 周期。

关闭空闲主动 PING 通常不产生云厂商额外费用,但连接重建会增加握手和 CPU;若因此改动负载均衡、监控或实例数量,费用以当前平台账单预估为准。

结果怎样判断

结果判断下一步
RST 不再按原 PING 周期出现,请求正常与已知兼容性现场一致分批推广并继续观察连接数和延迟
仍按固定周期重置还有其他实例发送空闲数据,或配置未生效核对生效配置、进程版本和流量落点
活跃 RPC 失败或出现 502/504不是单纯空闲连接抖动立即回退,查上游退出、超时和网络
只减少重置但连接数持续上涨空闲超时或连接池容量需要单独调整按并发和 worker 数重新核算 keepalive

接受重连也比不安全补丁更稳妥

维护者复现指出,原始 NGINX 行为会带来连接抖动,但测试请求仍得到正确响应。简单修改源码,让 idle handler 在看到数据时继续保留连接,会让未消费字节留在 socket 缓冲区;下一次请求复用时可能把旧字节当作新响应,造成请求失败,甚至产生跨请求响应混淆。因此在正式修复发布前,宁可接受可观测的重连,也不要部署这一行补丁。

验证、回退与上线检查

灰度后连续发起正常 RPC,再让连接空闲超过原 PING 周期和 NGINX 空闲超时。确认 RPC 成功率、P95 延迟、上游连接创建率、RST 数量与 TcpExtTCPAbortOnClose 不再同步异常增加。检查 NGINX 配置语法:

sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status nginx --no-pager

本文首选改动在 gRPC 服务端,不要求修改 NGINX。若灰度出现长连接异常、连接数飙升或延迟升高,把服务端 keepalive 配置恢复为原值并滚动回退;不要删除抓包和脱敏日志,保留给后续版本复核。上游 Issue 截至核验日仍开放且没有正式修复版本,升级前必须重新查看发布说明和实际版本行为。

相关排障

官方与上游来源

来源核验日期:2026-10-14。黑鲨云不是 NGINX 或 gRPC 官方。本文不把单个 Issue 泛化为所有 RST 的原因,也不宣称未发布修复已经可用。本稿未发现需要补充确认的黑鲨云业务事实。

云服务器教程Docker 拉取镜像时遇到 lease does not exist 怎么排查2026-10-15云服务器教程BuildKit prune 后 metadata_v2.db 还在变大怎么处理2026-10-13云服务器教程Docker Swarm 加密网络存在 VXLAN 注入漏洞怎么办2026-10-11

加入开发者交流社区

与全球开发者、运维和工作室一起交流技术、分享经验、配置、账号与最新优惠信息

  • 云平台使用交流
  • 资源优惠信息
  • 最新教程与资讯
  • 开发者经验分享
联系 Telegram 客服
加入开发者交流社区