前言前段时间业务侧反馈线上部分用户调用订单查询接口偶尔会出现请求超时。问题很棘手复现概率极低测试环境无论怎么压测都无法复现只有真实用户流量下不定时触发一天可能出现三五次也可能半天毫无动静。最开始团队默认是数据库慢 SQL 导致加索引、拆分大 SQL 一轮操作做完告警依旧零星弹出。前后断断续续花了四天逐层拆链路定位根因踩了不少容易被忽略的隐性坑整理完整排查链路记录下来方便后续遇到同类无头案对照复盘。一、初步排查常规链路摸排全部无有效线索服务整体架构是 SpringBoot 微服务通过 Nginx 做反向代理后端依赖 MySQL、Redis接口链路用户请求→Nginx→网关→订单服务→MySQL 查订单基础数据 Redis 读取用户缓存。 第一步先拿监控大盘做基础筛查。查看接口 QPS、响应时长监控。绝大多数请求耗时稳定在 20~80ms超时请求耗时固定卡在网关设置的 3s 上限没有中间梯度耗时MySQL 慢查询日志全天拉取核对超时时间段没有对应慢 SQL所有相关查询执行时间都低于 100msexplain 检查索引命中正常不存在全表扫描Redis 监控查看命令耗时、连接数、命中率超时节点 Redis 各项指标平稳无命令阻塞、无网络丢包服务器硬件指标CPU 使用率日常 30% 上下内存剩余充足磁盘 IO 等待值很低带宽没有打满。常规数据库、缓存、机器资源全部排除。运维同学顺手检查了防火墙、安全组规则出入站规则固定没有临时限流策略也不存在特定 IP 被拦截的情况。这时出现第一个疑点超时只集中在部分地域用户同城机房用户几乎零超时跨运营商访问更容易踩坑。一开始猜测是公网网络抖动找云厂商拉取对应时段网络链路质量报告运营商侧无丢包、无链路故障否定公网链路问题。二、分层抓包锁定卡顿出现在网关到业务服务之间既然整体链路过长决定分层抓包缩小范围。分三个节点抓包用户端、Nginx 网关服务器、订单服务后端服务器。 筛选超时请求的唯一 requestId对照时间戳核对数据包流转用户端发包到 Nginx耗时正常数据包完整送达Nginx 向订单服务发起 TCP 请求出现大量 SYN 重传三次握手迟迟无法完成握手失败后网关等待超时直接返回请求异常订单服务本身此时负载正常同服务器其他接口调用毫无阻碍。到此问题收敛不是应用代码、数据库问题是网关和后端服务之间 TCP 连接偶尔建连失败。 登录后端机器查看 TCP 连接状态大量 TIME_WAIT 连接堆积。用ss -s统计高峰期 TIME_WAIT 数量冲到两万多远超日常均值。为什么 TIME_WAIT 过多会导致新建连接失败我们服务网关调用后端用短连接模式每次接口请求新建 TCP 连接调用结束主动关闭连接连接进入 TIME_WAIT 状态默认等待 2MSL 时长释放端口。服务器本地可用端口区间有限大量连接卡在 TIME_WAIT 会快速耗尽临时端口池。端口不够新 TCP 连接无法发起网关侧持续重传握手包最终超时。之前一直默认长连接更消耗资源为了省事全链路用短连接没考虑到高峰期接口调用频次高端口复用跟不上出现隐性端口耗尽。这也是问题偶发的关键只有短时间内接口调用密集TIME_WAIT 堆积到阈值才会触发流量平缓时一切正常。三、落地两套优化方案分线上灰度验证敲定优化方向分内核参数调优 应用连接池改造双线落地先灰度小流量放量观察无异常再全量发布。1. Linux 内核 TCP 参数调整临时应急所有后端机器统一配置修改 /etc/sysctl.conf调整核心四项参数plaintextnet.ipv4.tcp_tw_reuse 1 net.ipv4.tcp_tw_recycle 0 net.ipv4.ip_local_port_range 1024 65535 net.ipv4.tcp_fin_timeout 30开启 tcp_tw_reuse允许复用 TIME_WAIT 状态的端口用于新建出站连接关闭 tcp_tw_recycle避免 NAT 环境下出现连接异常扩大本地可用端口范围拉高端口上限缩短 FIN 等待超时时间加快无用连接回收。 执行 sysctl -p 生效调整完成后TIME_WAIT 数量直接下降 70%。2. 应用层面根治网关与后端服务改用 HTTP 长连接SpringCloud 网关默认短连接调用修改网关 Feign 配置开启 HTTP 连接池复用 TCP 长连接从根源减少频繁建连、断连。 核心配置节选yamlfeign: httpclient: enabled: true max-connections: 200 max-connections-per-route: 50引入 Apache HttpClient 依赖替换默认 URLConnection限制单路由最大连接数保证连接可控不会无限膨胀。改造后网关不再频繁创建销毁 TCP 连接TIME_WAIT 不再随流量上涨堆积。四、上线观测与遗留细节优化灰度放量半天线上接口超时告警直接清零连续三天监控巡检偶发超时彻底消失。跨运营商用户访问稳定性和内网用户基本无差别。 后续顺带补充两处兜底优化防止同类问题复发新增机器 TIME_WAIT 连接数监控告警设置阈值一旦连接数量超过一万主动推送告警不用等业务报错再排查梳理全链路所有微服务调用方式统一规范服务内网调用全部使用长连接只有对外第三方接口保留短连接整理公司服务器 TCP 内核标准化模板新上线机器直接预装这套参数避免后续新服务踩同款坑。五、复盘总结程序员排查线上偶发故障容易踩的思维定式这次排查耗时久本质是惯性思维困住了排查节奏整理几点实操感悟接口超时优先想到 SQL、Redis、代码逻辑很容易忽略 TCP 层、操作系统内核这类底层网络问题。应用层监控完善但 TCP 连接、端口、内核参数往往缺少常态化监控偶发故障优先分层抓包不要靠猜。全链路监控只能看应用耗时TCP 握手、数据包重传这类底层细节必须依赖抓包定位短连接不是万金油。很多人觉得短连接轻量化不用维护连接池内网高频服务间调用场景长连接稳定性远优于短连接端口耗尽是高频隐性故障线上环境不要随意开启 tcp_tw_recycle。现在大量用户使用路由器 NAT 上网开启这个参数会导致跨 NAT 设备的正常连接被拒绝线上只推荐开启 tw_reuse。线上故障排查没有万能流程慢 SQL、缓存、硬件资源是高频原因但底层网络、操作系统参数这类冷门问题一旦出现复现难、隐蔽性极强。遇到无规律卡顿不要死磕业务代码往下走到网络层、系统层多做验证。后续维护打算把 TCP 相关指标纳入日常巡检清单把底层故障的发现前置不用等到业务报错被动救火。
线上服务频繁偶发超时?复盘一次生产环境无规律接口卡顿排查全流程
前言前段时间业务侧反馈线上部分用户调用订单查询接口偶尔会出现请求超时。问题很棘手复现概率极低测试环境无论怎么压测都无法复现只有真实用户流量下不定时触发一天可能出现三五次也可能半天毫无动静。最开始团队默认是数据库慢 SQL 导致加索引、拆分大 SQL 一轮操作做完告警依旧零星弹出。前后断断续续花了四天逐层拆链路定位根因踩了不少容易被忽略的隐性坑整理完整排查链路记录下来方便后续遇到同类无头案对照复盘。一、初步排查常规链路摸排全部无有效线索服务整体架构是 SpringBoot 微服务通过 Nginx 做反向代理后端依赖 MySQL、Redis接口链路用户请求→Nginx→网关→订单服务→MySQL 查订单基础数据 Redis 读取用户缓存。 第一步先拿监控大盘做基础筛查。查看接口 QPS、响应时长监控。绝大多数请求耗时稳定在 20~80ms超时请求耗时固定卡在网关设置的 3s 上限没有中间梯度耗时MySQL 慢查询日志全天拉取核对超时时间段没有对应慢 SQL所有相关查询执行时间都低于 100msexplain 检查索引命中正常不存在全表扫描Redis 监控查看命令耗时、连接数、命中率超时节点 Redis 各项指标平稳无命令阻塞、无网络丢包服务器硬件指标CPU 使用率日常 30% 上下内存剩余充足磁盘 IO 等待值很低带宽没有打满。常规数据库、缓存、机器资源全部排除。运维同学顺手检查了防火墙、安全组规则出入站规则固定没有临时限流策略也不存在特定 IP 被拦截的情况。这时出现第一个疑点超时只集中在部分地域用户同城机房用户几乎零超时跨运营商访问更容易踩坑。一开始猜测是公网网络抖动找云厂商拉取对应时段网络链路质量报告运营商侧无丢包、无链路故障否定公网链路问题。二、分层抓包锁定卡顿出现在网关到业务服务之间既然整体链路过长决定分层抓包缩小范围。分三个节点抓包用户端、Nginx 网关服务器、订单服务后端服务器。 筛选超时请求的唯一 requestId对照时间戳核对数据包流转用户端发包到 Nginx耗时正常数据包完整送达Nginx 向订单服务发起 TCP 请求出现大量 SYN 重传三次握手迟迟无法完成握手失败后网关等待超时直接返回请求异常订单服务本身此时负载正常同服务器其他接口调用毫无阻碍。到此问题收敛不是应用代码、数据库问题是网关和后端服务之间 TCP 连接偶尔建连失败。 登录后端机器查看 TCP 连接状态大量 TIME_WAIT 连接堆积。用ss -s统计高峰期 TIME_WAIT 数量冲到两万多远超日常均值。为什么 TIME_WAIT 过多会导致新建连接失败我们服务网关调用后端用短连接模式每次接口请求新建 TCP 连接调用结束主动关闭连接连接进入 TIME_WAIT 状态默认等待 2MSL 时长释放端口。服务器本地可用端口区间有限大量连接卡在 TIME_WAIT 会快速耗尽临时端口池。端口不够新 TCP 连接无法发起网关侧持续重传握手包最终超时。之前一直默认长连接更消耗资源为了省事全链路用短连接没考虑到高峰期接口调用频次高端口复用跟不上出现隐性端口耗尽。这也是问题偶发的关键只有短时间内接口调用密集TIME_WAIT 堆积到阈值才会触发流量平缓时一切正常。三、落地两套优化方案分线上灰度验证敲定优化方向分内核参数调优 应用连接池改造双线落地先灰度小流量放量观察无异常再全量发布。1. Linux 内核 TCP 参数调整临时应急所有后端机器统一配置修改 /etc/sysctl.conf调整核心四项参数plaintextnet.ipv4.tcp_tw_reuse 1 net.ipv4.tcp_tw_recycle 0 net.ipv4.ip_local_port_range 1024 65535 net.ipv4.tcp_fin_timeout 30开启 tcp_tw_reuse允许复用 TIME_WAIT 状态的端口用于新建出站连接关闭 tcp_tw_recycle避免 NAT 环境下出现连接异常扩大本地可用端口范围拉高端口上限缩短 FIN 等待超时时间加快无用连接回收。 执行 sysctl -p 生效调整完成后TIME_WAIT 数量直接下降 70%。2. 应用层面根治网关与后端服务改用 HTTP 长连接SpringCloud 网关默认短连接调用修改网关 Feign 配置开启 HTTP 连接池复用 TCP 长连接从根源减少频繁建连、断连。 核心配置节选yamlfeign: httpclient: enabled: true max-connections: 200 max-connections-per-route: 50引入 Apache HttpClient 依赖替换默认 URLConnection限制单路由最大连接数保证连接可控不会无限膨胀。改造后网关不再频繁创建销毁 TCP 连接TIME_WAIT 不再随流量上涨堆积。四、上线观测与遗留细节优化灰度放量半天线上接口超时告警直接清零连续三天监控巡检偶发超时彻底消失。跨运营商用户访问稳定性和内网用户基本无差别。 后续顺带补充两处兜底优化防止同类问题复发新增机器 TIME_WAIT 连接数监控告警设置阈值一旦连接数量超过一万主动推送告警不用等业务报错再排查梳理全链路所有微服务调用方式统一规范服务内网调用全部使用长连接只有对外第三方接口保留短连接整理公司服务器 TCP 内核标准化模板新上线机器直接预装这套参数避免后续新服务踩同款坑。五、复盘总结程序员排查线上偶发故障容易踩的思维定式这次排查耗时久本质是惯性思维困住了排查节奏整理几点实操感悟接口超时优先想到 SQL、Redis、代码逻辑很容易忽略 TCP 层、操作系统内核这类底层网络问题。应用层监控完善但 TCP 连接、端口、内核参数往往缺少常态化监控偶发故障优先分层抓包不要靠猜。全链路监控只能看应用耗时TCP 握手、数据包重传这类底层细节必须依赖抓包定位短连接不是万金油。很多人觉得短连接轻量化不用维护连接池内网高频服务间调用场景长连接稳定性远优于短连接端口耗尽是高频隐性故障线上环境不要随意开启 tcp_tw_recycle。现在大量用户使用路由器 NAT 上网开启这个参数会导致跨 NAT 设备的正常连接被拒绝线上只推荐开启 tw_reuse。线上故障排查没有万能流程慢 SQL、缓存、硬件资源是高频原因但底层网络、操作系统参数这类冷门问题一旦出现复现难、隐蔽性极强。遇到无规律卡顿不要死磕业务代码往下走到网络层、系统层多做验证。后续维护打算把 TCP 相关指标纳入日常巡检清单把底层故障的发现前置不用等到业务报错被动救火。