theme: default themeName: "默认主题" title: "Nginx 502 Bad Gateway的7种死法和排查思路"
Nginx 502 Bad Gateway的7种死法和排查思路
线上服务突然502,这种事我见过太多次了。有的人一上来就重启Nginx,运气好可能管用5分钟,运气不好问题照样在。502不是Nginx的问题——它是Nginx告诉你:"我试过了,但后面的服务不行。"
这句话的意思是:Nginx作为反向代理,已经成功接收了客户端请求,但在把请求转发给上游服务(upstream)的时候出了问题。Nginx本身没挂,挂的是它后面的东西。
下面我把线上遇到过的7种502场景列出来,每种都配上具体现象、排查命令和解决方法。建议收藏,半夜出事了翻出来对照着查。
第一种:upstream超时
现象: 部分请求502,部分请求正常。被502的请求普遍耗时较长,后端服务本身是正常的。 原因: 后端处理请求的时间超过了Nginx配置的超时阈值,Nginx主动断开了连接并返回502。 排查:# 查看Nginx错误日志,搜索upstream timed out
grep "upstream timed out" /var/log/nginx/error.log tail -20
日志里能看到类似这样的内容:
upstream timed out (110: Connection timed out) while reading response header from upstream
解决: 调大超时参数,但别无脑改到300秒。根据业务实际情况来:
location /api/ {
proxy_connect_timeout 5s; proxy_send_timeout 60s; proxy_read_timeout 60s; proxy_buffering on; }
`proxy_connect_timeout`是连接upstream的超时时间,一般5秒足够。`proxy_read_timeout`是读取响应的超时时间,这个才是最常需要调的。如果你的API确实需要处理很长时间(比如导出大文件),那就调大。但如果正常请求不应该超过5秒还超时,那问题在后端,不是Nginx。
第二种:PHP-FPM进程耗尽
现象: 流量高峰期集中爆发502,低峰期完全正常。Nginx错误日志里有`Connection refused`或`no live upstreams`。 原因: PHP-FPM的`pm.max_children`设置太小,所有worker进程都在忙,新请求进不来。 排查:# 查看PHP-FPM当前进程数
ps aux grep php-fpm grep -v grep wc -l
查看PHP-FPM状态页(需要先开启pm.status_path)
curl http://127.0.0.1:9000/status
状态页会显示`idle processes`为0,`active processes`等于`max children`。
解决: 调大`pm.max_children`,但要看服务器内存。每个PHP-FPM进程大约吃30-80MB内存(看你的应用有多重),算一下服务器能承载多少个:; php-fpm.d/www.conf
pm = dynamic pm.max_children = 100 pm.start_servers = 30 pm.min_spare_servers = 20 pm.max_spare_servers = 70 pm.max_requests = 1000
`pm.max_requests = 1000`也很关键。设置后每个worker处理1000个请求就自动重启,防止内存泄漏把进程搞成僵尸。
第三种:后端服务直接挂了
现象: 全量502,没有任何请求能正常处理。Nginx日志里是`Connection refused`。 原因: upstream服务进程崩了、没启动、或者端口没监听。 排查:# 检查后端服务是否在运行
systemctl status your-backend-service
检查端口是否在监听
ss -tlnp grep :8080
手动访问后端,看能不能通
curl -v http://127.0.0.1:8080/health
解决: 把后端服务拉起来。如果反复崩,去看后端服务的日志找崩溃原因。OOM、配置错误、代码bug都有可能。`journalctl -u your-backend-service --since "1 hour ago"`能帮你快速定位。
第四种:DNS解析失败
现象: Nginx配置里用了域名作为upstream地址,某些时候502,某些时候又正常。 原因: Nginx在启动时解析域名并缓存IP,如果上游服务的IP变了(比如容器化部署),Nginx还在连旧的IP。 排查:# 看Nginx错误日志
grep "could not resolve" /var/log/nginx/error.log grep "host not found" /var/log/nginx/error.log
手动解析看当前DNS返回什么
nslookup your-upstream.example.com dig your-upstream.example.com
解决: 如果用域名做upstream,配上`resolver`指令并开启定期解析:
resolver 8.8.8.8 114.114.114.114 valid=30s;
resolver_timeout 5s;
location / { set $upstream_addr your-backend.example.com; proxy_pass http://$upstream_addr; }
用了`set`变量之后,Nginx会在每次请求时重新解析域名(受`valid`控制缓存时间)。不用变量的话,Nginx只在启动时解析一次,后面IP怎么变它都不知道。
不过说实话,生产环境我更推荐用IP+端口或者内部DNS服务(比如Consul、CoreDNS),别依赖外部DNS做服务发现。
第五种:连接数耗尽
现象: 502和504交替出现,`netstat`显示大量`TIME_WAIT`或`CLOSE_WAIT`连接。 原因: Nginx和upstream之间的连接数达到了上限,或者系统层面的文件描述符/端口资源耗尽。 排查:# 查看Nginx到upstream的连接状态分布
ss -s
查看TIME_WAIT数量
ss -tan state time-wait wc -l
查看当前进程的fd使用量
ls /proc/$(cat /var/run/nginx.pid)/fd wc -l
cat /proc/$(cat /var/run/nginx.pid)/limits grep "open files"
解决: 两个方向,一个是调Nginx的`worker_connections`和`worker_rlimit_nofile`:
worker_rlimit_nofile 65535;
events { worker_connections 4096; }
另一个是优化keepalive复用连接,减少频繁建连的开销(第七种会详细说)。
如果是`TIME_WAIT`堆积的问题,系统层面可以开启连接复用:
# /etc/sysctl.conf
net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 15
第六种:buffer溢出
现象: 后端返回大量数据时502,普通小请求完全正常。Nginx错误日志里有`upstream sent too big header`。 原因: 后端返回的响应头或响应体超过了Nginx的buffer大小限制。 排查:grep "upstream sent too big" /var/log/nginx/error.log | tail -5
grep "client intended to send too large body" /var/log/nginx/error.log tail -5
解决: 根据日志提示调大对应的buffer:
location /api/ {
proxy_buffer_size 16k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; proxy_max_temp_file_size 0;
# 如果是响应头太大(比如Cookie/Set-Cookie很长) proxy_buffer_size 32k; }
`proxy_max_temp_file_size 0`的意思是当响应体超过内存buffer时,不写磁盘临时文件。这样做可以避免磁盘IO成为瓶颈,但代价是会阻塞后端——后端必须等Nginx把数据发给客户端才能继续。根据你的场景权衡。
第七种:keepalive配置不当
现象: 高并发下间歇性502,后端服务本身健康检查通过,但Nginx就是连不上。 原因: Nginx到upstream的keepalive长连接没有正确配置,导致每次请求都要重新建TCP连接。建连成本在高并发下被放大,来不及处理就超时了。 排查: 看Nginx到后端的连接建立频率:# 抓包看SYN包的频率
tcpdump -i any -n 'host your-upstream-ip and tcp[tcpflags] & tcp-syn != 0' -c 100
如果高并发下还在疯狂建连,说明keepalive没生效。
解决: 在upstream块里配置keepalive:upstream backend {
server 127.0.0.1:8080; keepalive 128; # 保持128个空闲长连接 keepalive_timeout 60s; keepalive_requests 1000; }
server { location / { proxy_http_version 1.1; proxy_set_header Connection ""; proxy_pass http://backend; } }
这里有个坑必须注意:`proxy_http_version 1.1`和`proxy_set_header Connection ""`必须同时配。HTTP/1.0不支持keepalive,而Nginx默认用HTTP/1.0连upstream。`Connection ""`是清掉Connection头的默认值(默认会发`Connection: close`),告诉upstream保持连接。这两个不配,keepalive形同虚设。
排查502的通用思路
其实不管哪种502,排查套路是一样的:
1. 看Nginx错误日志。`/var/log/nginx/error.log`是一切问题的起点。日志里的关键字决定了你接下来往哪个方向走。 2. 确认upstream状态。端口在不在、服务活没活、能不能连通。 3. 看系统资源。CPU、内存、文件描述符、连接数,哪个到了瓶颈。 4. 看配置。timeout、buffer、keepalive这些参数是否合理。
502不复杂,复杂的是你得在2分钟内找到根因然后恢复服务。所以平时多做演练,把这些排查命令练到形成肌肉记忆。
另外,强烈建议在Nginx配置里加上upstream健康检查:
upstream backend {
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; }
`max_fails=3`表示连续3次请求失败就把这个节点标记为不可用,`fail_timeout=30s`表示30秒后再试。这样即使后端挂了,Nginx也不会反复往死服务上送请求,直接返回502并快速失败,避免了雪崩。
【放心,我们兜底】
不管你是自己尝试修复,还是需要专业人员上门,易云城IT服务都给你托底。修不好不收费,修好了质保期内随时找我。
📞 服务热线:13708730161 💬 微信:eyc1689 📧 邮箱:service@eycit.com 🌐 https://www.eycit.com
您身边的IT专家。