Nginx 排错调优速查
Nginx 排错调优速查
[!abstract] 定位 Nginx 作为反向代理和负载均衡的核心,面试高频考点集中在:状态码排查、性能调优、TIME_WAIT 优化、限流配置。
一、HTTP 状态码速查
| 状态码 | 含义 | 根因 | 排查方向 |
|---|---|---|---|
| 502 | Bad Gateway | Nginx 无法连接后端(连接都没建立) | ss -tlnp 确认后端端口是否在监听;iptables -L 检查防火墙;后端是否 OOM 被杀 |
| 504 | Gateway Timeout | 连上了后端,但后端没在 proxy_read_timeout 内返回响应 | 后端慢查询?死锁?SHOW PROCESSLIST 看 MySQL;后端负载是否被打满 |
| 499 | Client Closed Request | 客户端主动断开(不等了) | 常见于移动端用户切后台/弱网;proxy_read_timeout 太长导致客户端超时先断 |
[!tip] 面试金句 「502 是敲门没人开,504 是开了门但站门口不让你进。」
二、TIME_WAIT 问题
为什么会有大量 TIME_WAIT?
TIME_WAIT 是 TCP 主动关闭方必然经历的状态,持续 2MSL(约 60s)。Nginx 作为反向代理,每次向后端发起短连接请求后主动关闭,就产生一个 TIME_WAIT。大量短连接 = 大量 TIME_WAIT。
怎么确认?
ss -ant | awk '{print $1}' | sort | uniq -c | sort -rn | head
# 如果 TIME_WAIT 数量 > 数万,说明有问题
# 或者看具体分布
ss -ant state time-wait | wc -l
内核参数调整
# /etc/sysctl.conf
# 1. 快速回收 TIME_WAIT(降低 reuse 门槛)
net.ipv4.tcp_tw_reuse = 1
# 2. ⚠️ tcp_tw_recycle — 永远不要开!
# 在 NAT 环境下会导致丢包(时间戳不一致的包被丢弃)
# net.ipv4.tcp_tw_recycle = 0 # 保持关闭
# 3. 扩大端口范围
net.ipv4.ip_local_port_range = 10240 65535
# 4. 增加 TIME_WAIT bucket 数量
net.ipv4.tcp_max_tw_buckets = 262144
# 5. 缩短 FIN_WAIT2 超时
net.ipv4.tcp_fin_timeout = 15
[!warning] 为什么不能开
tcp_tw_recycle? 面试如果提到这个,直接赢一半。因为tcp_tw_recycle依赖 TCP 时间戳来判断是否是旧连接,但在 NAT 环境下不同客户端的包可能时间戳差异很大,导致正常 SYN 包被内核丢弃。Nginx 作为反向代理几乎都在 NAT 场景下(SLB/负载均衡),开了必出诡异丢包。 Linux 4.12 内核已经彻底移除了这个选项。
Nginx 层面解决 — 用长连接
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
keepalive 32; # 关键!保持 32 个长连接复用
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1; # 必须 HTTP/1.1
proxy_set_header Connection ""; # 清空 Connection 头,让 keepalive 生效
}
}
设置 keepalive 后,Nginx 和后端之间用长连接复用,不再每次请求新建/关闭连接,TIME_WAIT 问题从根本上解决。
三、紧急止血处置
场景:502 大规模爆发
graph TB
A[502 告警] --> B{确认后端进程}
B -->|进程正常| C[curl 本地测试<br/>curl -v localhost:8080/health]
B -->|进程死了| D[重启后端服务]
C -->|正常| E{Nginx 能连吗?}
E -->|能| F[检查 proxy_read_timeout<br/>是否太短]
C -->|不通| D
E -->|不能| G{防火墙/安全组?}
G -->|iptables 规则| H[iptables -L -n 检查]
G -->|安全组| I[检查云控制台安全组规则]
紧急操作优先级
| 优先级 | 操作 | 命令 | 适用场景 |
|---|---|---|---|
| 🥇 | 扩容后端 | 扩大 K8s 副本数 / 加机器 | 后端负载高 |
| 🥈 | Nginx 热重载 | nginx -s reload | 配置变更 |
| 🥉 | 切流量 | DNS / SLB 摘掉故障节点 | 单机故障 |
| ❌ | 重启 Nginx | systemctl restart nginx | 最后手段,会丢连接 |
[!warning] 别一上来就重启 Nginx
systemctl restart会断开所有现有连接,nginx -s reload才是正确的平滑重载。
四、常用调优配置
# Nginx 核心调优参数
worker_processes auto; # CPU 核数
worker_rlimit_nofile 65535; # 文件描述符上限
events {
use epoll;
worker_connections 4096; # 单 worker 最大连接数
multi_accept on; # 一次接受多个连接
}
http {
# 代理超时
proxy_connect_timeout 5s; # 连接后端超时(默认 60s 太长了)
proxy_read_timeout 30s; # 读后端响应超时
proxy_send_timeout 30s;
# 缓冲区
proxy_buffer_size 4k;
proxy_buffers 8 4k;
# 限流
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=100r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
location /api/ {
limit_req zone=mylimit burst=20 nodelay; # 突发 20,超出直接 503
limit_conn addr 10; # 单 IP 最多 10 并发
}
}
}
五、快速诊断脚本
#!/bin/bash
# nginx-diag.sh
echo "=== Nginx 进程 ==="
ps aux | grep nginx | grep -v grep
echo ""
echo "=== 连接状态统计 ==="
ss -ant | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
echo ""
echo "=== 端口监听 ==="
ss -tlnp | grep nginx
echo ""
echo "=== 最近 502/504 请求(需开启 access_log) ==="
tail -100 /var/log/nginx/access.log | grep ' 502 \| 504 '
echo ""
echo "=== 错误日志最后 20 行 ==="
tail -20 /var/log/nginx/error.log
#sre #nginx #面试