监控告警:nginx 502错误率飙升到5%。
看了眼后端服务,运行正常,没有报错。重启nginx,好了一会又开始502。
排查了3个小时,最后发现是upstream配置的问题。记录一下排查过程。
问题现象
监控数据:
- 502错误率:从0.1% → 5%
- 后端服务:正常运行,无报错
- cpu/内存:正常
- 发生时间:流量高峰期
特点:
- 不是全部请求都502,大部分正常
- 重启nginx后短暂恢复,然后又出现
- 后端服务日志没有异常
排查过程
step 1:看nginx错误日志
tail -f /var/log/nginx/error.log
发现大量这样的错误:
upstream timed out (110: connection timed out) while connecting to upstream upstream prematurely closed connection while reading response header
关键信息:是upstream连接的问题,不是后端服务本身的问题。
step 2:检查后端服务状态
# 查看后端服务进程 ps aux | grep java # 查看端口监听 ss -tlnp | grep 8080 # 直接测试后端 curl -i http://127.0.0.1:8080/health
后端服务正常,直接访问返回200。
step 3:检查连接数
# 查看nginx到后端的连接数
ss -ant | grep 8080 | wc -l
# 查看连接状态分布
ss -ant | grep 8080 | awk '{print $1}' | sort | uniq -c发现问题了:
850 established
120 time_wait
50 syn_sent有50个连接卡在syn_sent状态,说明nginx到后端的新连接建立不上。
step 4:检查后端连接队列
# 查看后端服务的accept队列 ss -lnt | grep 8080
输出:
state recv-q send-q local address:port listen 129 128 0.0.0.0:8080
问题找到了! recv-q是129,send-q是128。
这说明accept队列满了(128是默认值),新连接无法被接受。
根因分析
什么是accept队列
客户端 → syn → 服务端(半连接队列) 服务端 → syn+ack → 客户端 客户端 → ack → 服务端(全连接队列/accept队列) 应用程序 accept() → 取出连接
当accept队列满了,新的完成三次握手的连接无法进入队列,客户端会收到超时或rst。
为什么队列满了
后端是spring boot应用,默认配置:
server:
tomcat:
accept-count: 100 # tomcat的accept队列而系统层面的限制是net.core.somaxconn = 128,取两者较小值,所以实际accept队列只有128。
流量高峰时:
- 请求量大,新连接多
- accept队列128不够用
- 新连接被拒绝
- nginx收到超时,返回502
解决方案
方案一:调大系统参数
# 查看当前值 sysctl net.core.somaxconn # 临时修改 sysctl -w net.core.somaxconn=65535 # 永久修改 echo "net.core.somaxconn = 65535" >> /etc/sysctl.conf sysctl -p
方案二:调整tomcat配置
server:
tomcat:
accept-count: 1000 # accept队列大小
max-connections: 10000 # 最大连接数
threads:
max: 500 # 最大工作线程数方案三:nginx upstream优化
upstream backend {
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
keepalive 100; # 保持连接数,减少新建连接
}
server {
location / {
proxy_pass http://backend;
proxy_connect_timeout 5s; # 连接超时
proxy_read_timeout 60s; # 读取超时
proxy_send_timeout 60s; # 发送超时
proxy_http_version 1.1; # 使用http/1.1
proxy_set_header connection ""; # 配合keepalive
}
}方案四:多实例负载均衡
如果单实例撑不住,可以部署多实例:
upstream backend {
least_conn; # 最少连接数策略
server 127.0.0.1:8080 weight=1;
server 127.0.0.1:8081 weight=1;
server 127.0.0.1:8082 weight=1;
keepalive 100;
}最终配置
系统参数(/etc/sysctl.conf):
net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 65535 net.core.netdev_max_backlog = 65535
spring boot配置:
server:
tomcat:
accept-count: 2000
max-connections: 20000
threads:
max: 500
min-spare: 50nginx配置:
upstream backend {
server 127.0.0.1:8080;
keepalive 200;
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header connection "";
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
}
}优化效果
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 502错误率 | 5% | 0.01% |
| accept队列溢出 | 频繁 | 无 |
| 连接建立时间 | 不稳定 | 稳定<5ms |
排查命令汇总
# 查看nginx错误日志 tail -f /var/log/nginx/error.log # 查看连接状态 ss -ant | grep <端口> # 查看监听队列 ss -lnt | grep <端口> # 查看队列溢出统计 netstat -s | grep -i listen # 查看系统参数 sysctl net.core.somaxconn # 实时监控连接数 watch -n 1 'ss -ant | grep <端口> | wc -l'
经验总结
| 502原因 | 排查方向 |
|---|---|
| upstream timed out | 后端处理慢或连接队列满 |
| connection refused | 后端服务没启动 |
| no live upstreams | 所有后端都不可用 |
| prematurely closed | 后端主动断开连接 |
这次的坑:后端服务看起来正常,但accept队列满了,新连接进不来。
教训:
- 系统默认的
somaxconn=128太小,生产环境必须调大 - nginx配置
keepalive可以减少新建连接,降低队列压力 - 监控要加上连接队列指标
以上就是线上nginx频繁502的排查过程与解决方案的详细内容,更多关于线上nginx频繁502排查的资料请关注代码网其它相关文章!
发表评论