华为云ELB偶发502排查

举报
yd_221506059 发表于 2026/10/17 13:03:20 2026/10/17
【摘要】 502 率只有 0.05%,却在低峰期集中出现,后端应用日志里翻不到对应请求。本文先区分 502 与 504 的排查方向,再从机制上解释 keepalive 连接复用与关闭竞争:后端先关连接、ELB 不知情仍复用,请求就撞上 RST。文中给出对齐超时参数、抓包看 FIN 与请求时序、观察连接数锯齿三个验证动作,以及让 ELB 先关、后端后关的修复原则,并附其他六种 502 场景的分类清单。

华为云 ELB 偶发 502 排查:问题不在后端,在"连接被谁先关掉"

502 率只有 0.05%,而且高峰期完全正常、低峰期反而集中出现。后端应用日志里翻不到对应请求,代码、CPU、内存、慢查询全部干净。最后查出来是 ELB 与后端之间的长连接超时配置错配,一个参数改完,502 归零。

一、现象:一个反常识的分布

线上接口通过 ELB 暴露,监控里出现 502,特征是:

观察项 现象
502 率 0.05% 左右,绝对量不大但持续存在
时间分布 集中在低峰期(凌晨 2~6 点),高峰期几乎为 0
后端应用日志 找不到对应的请求记录
后端资源 CPU、内存、GC、连接池全部正常
下游依赖 无超时、无慢查询

第三条是决定性线索:后端应用根本没收到这个请求。所以问题不在业务代码,而在"请求到达后端之前"的那一段——也就是 ELB 到后端之间的连接。

二、先把 502 和 504 分清楚

这两个错误码经常被混在一起排查,方向完全不同:

错误码 含义 排查方向
502 Bad Gateway 网关从后端拿到了无效响应,或连接异常 连接层:RST、连接被关、响应格式非法
504 Gateway Timeout 网关等后端超时 应用层:慢查询、线程池打满、死锁

本次是 502,且后端无日志,指向"连接层异常"——ELB 把请求发到了一条已经被关闭的连接上,写进去立刻收到 RST,于是判定上游异常,返回 502。

三、机制:keepalive 复用与"谁先关闭"

ELB 与后端之间不是每次请求都新建连接,而是维护一个长连接池,请求来了从池里取一条复用。这套机制在绝大多数时候都很好,但它有一个隐含前提:

连接池里的连接,必须是活的。

问题就出在"谁先判定这条连接已经没用了"上。连接的两端各自维护自己的空闲超时:

  • ELB 侧:监听器的空闲超时(比如 300 秒),超过就关闭连接
  • 后端侧:keepalive 超时(比如 60 秒),超过就关闭连接

如果后端先关(60 秒 < 300 秒),会发生这样的竞争:

T+60s   后端认为连接空闲超时,主动发 FIN 关闭连接
        ↓ 但 ELB 的空闲计时器还没到(它以为还有 240 秒)
T+61s   ELB 收到请求,从池里取出这条连接,写入请求数据
        ↓ 后端早已关闭,回 RST
T+61s   ELB 判定上游连接异常 → 返回 502

关键在于"关闭的感知是单向的":后端关闭连接时,ELB 在收到 FIN 之前仍然认为这条连接可用,于是出现了一个竞争窗口。窗口里的请求,全部变成 502。

为什么低峰期更严重

这就是整件事最反常识的地方,也是验证这个假设的最好证据:

  • 高峰期:连接一直在传数据,几乎不会触发空闲超时,池里的连接始终是活的 → 502 极少
  • 低峰期:请求稀疏,连接频繁进入空闲状态,大量连接恰好落在"后端已关闭、ELB 未感知"的窗口里 → 502 集中爆发

“流量越少错误越多”,基本可以把连接复用类问题直接锁定。

四、验证:三个动作确认假设

1. 对齐两边的超时配置

把 ELB 监听器的空闲超时,和后端服务的 keepalive 超时并排写出来对比:

配置项 值 关系
ELB 监听器空闲超时 300 s 较长
后端 Nginx keepalive_timeout 60 s 较短 → 后端先关 → 危险

只要后端超时小于 ELB 超时,就一定存在这个竞争窗口。

2. 在后端抓包,看 FIN 和请求的时序

# 在后端服务器抓 ELB 过来的流量
tcpdump -i any -nn -s0 'tcp port 8080' -w /tmp/elb.pcap

在 Wireshark 里过滤 tcp.flags.fin == 1 || tcp.flags.reset == 1,能看到:

  • 后端发出 FIN, ACK(空闲超时主动关闭)
  • 几十毫秒后,ELB 在这条连接上发送 HTTP 请求
  • 后端回 RST

这个时序一旦出现,假设就成立。

3. 看后端连接数是否周期性归零

后端 ESTABLISHED 连接数如果呈锯齿状、周期性掉到接近 0,说明连接被批量关闭了——这是 keepalive 超时在起作用。正常情况下,连接数应该稳定在一个水平线上小幅波动。

五、修复:让 ELB 先关,后端后关

原则只有一条:

关闭动作必须由连接池的持有者(ELB)发起。

因为 ELB 关闭自己池里的连接时,会同步把它从池中移除,不会再把请求发到死连接上。反过来,后端擅自关闭,ELB 就成了"不知情的一方"。

落地方式:

做法 说明
后端 keepalive 超时 > ELB 空闲超时 例如 ELB 300 s,后端设 900 s,留足余量
或调小 ELB 空闲超时 让它明显短于后端超时,同样成立
后端限制单连接最大请求数 如 Nginx keepalive_requests 1000,主动轮换,避免长连接老化

后端 Nginx 侧示例:

upstream app_backend {
    server 192.168.1.10:8080;
    keepalive 64;              # 连接池大小
    keepalive_requests 1000;   # 单连接最多处理 1000 个请求后轮换
    keepalive_timeout 900s;    # 必须大于 ELB 空闲超时
}

同时把 proxy_http_version 1.1 和 proxy_set_header Connection "" 配上,否则 Nginx 对上游默认用 HTTP/1.0,keepalive 根本不生效:

proxy_http_version 1.1;
proxy_set_header Connection "";

这一条经常被漏掉:配了 keepalive 却没改 HTTP 版本,等于没配,连接照样每次新建。

六、其他会引发 502 的场景(分类清单)

连接复用只是 502 的一个来源。按出现频率排一下,方便下次快速定位:

场景 特征 处理
超时错配(本文) 低峰期集中、后端无日志 后端 keepalive > ELB 空闲超时
滚动发布未优雅摘流 发布时间点集中出现 优雅停机 + 健康检查摘流延迟
后端连接队列溢出 高并发时突发,伴随 SYN 重传 调大 backlog、扩实例
健康检查配置错误 只有部分实例持续 502 核对健康检查路径与端口
响应头过大 / 非法响应 特定接口必现 检查 header 体积、响应格式
后端 TLS 配置不一致 改了证书后开始出现 对齐协议版本与证书链

排查顺序建议:先看 502 是否与流量相关(低峰多还是高峰多),这一步就能把范围砍掉一大半。

七、效果

指标 修复前 修复后
502 率 0.05% 0
低峰期 502 次数 每小时数十次 0
后端 ESTABLISHED 连接数 锯齿状,周期性归零 平稳
改动内容 — 后端 keepalive 60s → 900s,补 proxy_http_version 1.1

代码零改动,业务零感知。

八、小结

四句话:

  1. 502 看连接,504 看超时,先把错误码分清楚,排查方向完全不同。
  2. "流量越少错误越多"是连接复用类问题的强信号,空闲超时在低峰期才暴露。
  3. 关闭动作必须由连接池持有者发起——后端 keepalive 超时要大于 ELB 空闲超时,方向别搞反。
  4. 配了 keepalive 别忘了 proxy_http_version 1.1,否则连接池形同虚设。

如果只记一句:偶发 502 先去对齐两边的超时参数,比翻应用日志快得多。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。