文章

Nginx 排错调优速查

Nginx 排错调优速查

[!abstract] 定位 Nginx 作为反向代理和负载均衡的核心,面试高频考点集中在:状态码排查、性能调优、TIME_WAIT 优化、限流配置。


一、HTTP 状态码速查

状态码含义根因排查方向
502Bad GatewayNginx 无法连接后端(连接都没建立)ss -tlnp 确认后端端口是否在监听;iptables -L 检查防火墙;后端是否 OOM 被杀
504Gateway Timeout连上了后端,但后端没在 proxy_read_timeout 内返回响应后端慢查询?死锁?SHOW PROCESSLIST 看 MySQL;后端负载是否被打满
499Client 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 摘掉故障节点单机故障
重启 Nginxsystemctl 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 #面试