2023 年我们做一个直播弹幕推送系统 业务量从 1 万 QPS 涨到 30 万 QPS 用 Nginx 做反代 + 静态资源 + WebSocket upgrade。第一版直接 apt install nginx 改个 server_name 就上线 老板说"Nginx 这么稳" 直播开播 5 分钟 Nginx 直接 502 全站崩。第一种最让我傻眼是 worker_connections 默认 1024 30 万并发立刻 too many open files;第二种最难缠是 worker_processes auto 实际开 32 worker 16 核 CPU context switch 把内核打懵 P99 延迟从 5ms 涨到 800ms;第三种最离谱是 upstream keepalive 没配 每个上游请求新建 TCP 后端 TIME_WAIT 几万个 SYN flood 警报;第四种最致命是 buffer 参数全用默认 大文件上传内存爆 OOM Killer 把 Nginx 干掉;第五种最莫名其妙是 gzip 开了 on 但所有 CSS/JS/HTML 都没压缩 排查发现 gzip_types 没设 默认只压 text/html;第六种最坑是 access log 同步写磁盘 30 万 QPS IO wait 50% 整个 Nginx 卡成 PPT。真正能扛超大并发的 Nginx 不是 apt install 后改个 server_name 就够,而是一个 worker 模型调优 + 连接池 + buffer 治理 + 压缩缓存 + 日志异步 + 内核 sysctl + reload 平滑 + 监控告警的完整工程体系,任何一环失守都可能让你的"稳如老狗的 Nginx"在流量峰值瞬间崩溃。本文从踩坑视角梳理 Nginx 生产化调优要点,worker 怎么配 连接怎么池化 buffer 怎么调 缓存怎么做 日志怎么异步 内核怎么调 reload 怎么平滑,以及一些把 Nginx 做扎实要避开的工程坑。
问题背景:为什么 apt install nginx 远远不够
很多团队装 Nginx 是 apt install + 改 server_name 就完事 流量小时风平浪静 流量一大全是雷:
- Worker 模型:worker_processes / worker_connections 直接决定并发上限。
- 连接池:upstream keepalive 没配会让后端疯狂建连 TIME_WAIT 堆积。
- Buffer 配置:client_body / proxy_buffer / large_client_header 出错就 OOM 或 502。
- 压缩与缓存:gzip / brotli / proxy_cache 能省 80% 带宽 不配等于裸跑。
- 日志 IO:同步 access log 在大流量下是 IO 杀手 必须 buffer + flush。
- 内核调优:somaxconn / tcp_max_syn_backlog / file-max 等 sysctl 不调 Nginx 再快也被卡。
一 Worker 模型与连接数
worker_processes 与 worker_connections 是 Nginx 并发能力的两个核心旋钮 配错代价巨大。
# /etc/nginx/nginx.conf
# 1 worker 进程数:cpu 数 不要 auto 盲信
# auto 在容器里会读 host cpu 数(可能 64) 但 cgroup 只给 4 核 必崩
worker_processes 16; # 显式设 = 实际可用 cpu 核
# 2 cpu 绑核(避免 cpu cache miss)
worker_cpu_affinity auto;
# 老版本写法
# worker_cpu_affinity 0000000000000001 0000000000000010 0000000000000100 ...
# 3 worker 优先级(降低被抢占)
worker_priority -10;
# 4 进程级 fd 上限
worker_rlimit_nofile 1000000; # 必须 = OS ulimit -n 否则浪费
events {
# 5 单 worker 最大并发连接
worker_connections 65535; # 30 万并发需要 16 * 65535 = 100 万 fd
use epoll; # Linux 必 epoll
multi_accept on; # 一次 accept 多个连接 减少 epoll wakeup
# 6 accept 锁(高并发时减少惊群)
accept_mutex off; # 新内核 SO_REUSEPORT 后关掉更好
accept_mutex_delay 100ms;
}
http {
# 7 sendfile / tcp_nopush / tcp_nodelay
sendfile on; # 零拷贝大文件
tcp_nopush on; # 配合 sendfile 攒满再发
tcp_nodelay on; # 长连接禁 Nagle
# 8 keepalive
keepalive_timeout 65;
keepalive_requests 10000; # 默认 100 太低 长连接被反复重建
# 9 server 级监听用 SO_REUSEPORT(内核负载均衡)
server {
listen 80 reuseport; # 内核分发连接到各 worker 不抢锁
listen 443 ssl http2 reuseport;
}
}
实战经验:worker_processes auto 在容器里是坑 cgroup 限 4 核它开 64 worker 必崩 必须显式设;worker_rlimit_nofile 必须配合 OS 的 /etc/security/limits.conf 改 nofile 否则 Nginx 自己限自己;worker_connections 65535 + 16 worker = 100 万并发 但每连接 4-8KB 内存 算清楚再开;SO_REUSEPORT 是 Linux 3.9+ 的杀手锏 内核层负载均衡 比 accept_mutex 性能高 30%;keepalive_requests 默认 100 必改 大 QPS 长连接复用率才高。我们 worker 调优后 单机 QPS 从 8 万涨到 30 万 P99 从 800ms 降到 12ms。
二 Upstream 连接池与负载均衡
upstream keepalive 没配 Nginx 每个请求都 connect/close 后端 后端 TIME_WAIT 堆积是大流量第一杀手。
http {
# 1 upstream 定义(带连接池)
upstream backend_app {
# 负载均衡算法
# least_conn; # 最少连接(默认 round_robin)
# ip_hash; # session 粘性(简单但不优雅)
# hash $request_uri consistent; # 一致性哈希(缓存友好)
server 10.0.0.10:8080 weight=5 max_conns=2000 max_fails=2 fail_timeout=10s;
server 10.0.0.11:8080 weight=5 max_conns=2000;
server 10.0.0.12:8080 backup; # 备用
# 2 连接池(关键 默认是关的)
keepalive 200; # 每个 worker 保留 200 个空闲连接
keepalive_requests 10000; # 单连接最多复用次数
keepalive_timeout 60s;
}
server {
location / {
proxy_pass http://backend_app;
# 3 必须开 HTTP/1.1 否则 keepalive 不生效
proxy_http_version 1.1;
proxy_set_header Connection ""; # 默认 close 必须清掉
# 4 超时配置(防止慢后端拖死 Nginx)
proxy_connect_timeout 3s;
proxy_send_timeout 10s;
proxy_read_timeout 30s;
# 5 失败重试与降级
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 5s;
# 6 转发 header(client 信息透传)
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-ID $request_id;
}
}
# 7 长连接 WebSocket upstream
upstream ws_backend {
server 10.0.0.20:8081 max_conns=5000;
server 10.0.0.21:8081 max_conns=5000;
keepalive 500;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
location /ws/ {
proxy_pass http://ws_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s; # 长连接 1 小时
proxy_send_timeout 3600s;
}
}
# 8 健康检查(nginx_upstream_check_module 或商业版)
# 开源版只有被动健康(请求失败才标记 down)
# 主动健康需要 nginx-plus 或 OpenResty + lua-resty-checkups
}
实战经验:upstream keepalive 是大流量必上 否则后端 TIME_WAIT 几万个 ulimit 撞死;proxy_http_version 1.1 + Connection "" 必须配对 缺一不可 漏一个 keepalive 不生效;max_conns 限制单 server 并发 防止单点过载;一致性哈希(hash ... consistent)对后端缓存友好 同一 URL 永远落到同一后端;proxy_next_upstream 配 502/503/504 实现故障自愈 但 POST 默认不重试要显式加 non_idempotent;WebSocket 必须显式配 Upgrade header 默认 Nginx 不转发。我们加 upstream keepalive 后 后端 TIME_WAIT 从 5 万降到 200。
三 Buffer 与内存管理
buffer 配错要么 OOM 要么 502 大文件上传 / 大 header / 大响应都有专门的 buffer。
http {
# 1 客户端请求 body(POST 上传)
client_body_buffer_size 128k; # 超过写临时文件
client_max_body_size 100m; # 上传大文件改这里 不改 413
client_body_temp_path /var/cache/nginx/client_body 1 2;
client_body_timeout 30s;
# 2 客户端请求 header(防大 cookie)
client_header_buffer_size 4k;
large_client_header_buffers 4 16k; # 大 header 给 4 个 16k buffer
client_header_timeout 10s;
# 3 upstream 响应 buffer(proxy)
proxy_buffering on; # 关键 关掉就是流式
proxy_buffer_size 16k; # 第一个 buffer(放响应 header)
proxy_buffers 8 64k; # 8 个 64k buffer
proxy_busy_buffers_size 128k; # 已发送的 buffer 上限
proxy_temp_file_write_size 256k; # 写临时文件单次大小
proxy_max_temp_file_size 1024m; # 临时文件最大
proxy_temp_path /var/cache/nginx/proxy_temp 1 2;
# 4 流式响应(SSE / 大下载 必须关 buffering)
location /sse/ {
proxy_pass http://backend_app;
proxy_buffering off; # SSE / 长轮询必关
proxy_cache off;
proxy_set_header Connection "";
proxy_http_version 1.1;
chunked_transfer_encoding on;
keepalive_timeout 600s;
}
# 5 大文件下载(走 sendfile + open_file_cache)
open_file_cache max=200000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
location /downloads/ {
sendfile on;
sendfile_max_chunk 1m;
directio 4m; # 大文件绕过 PageCache
output_buffers 2 32k;
}
# 6 大上传(防 OOM 写临时文件)
location /upload/ {
client_body_buffer_size 1m;
client_max_body_size 2g;
client_body_temp_path /data/upload_tmp 1 2;
proxy_pass http://upload_backend;
proxy_request_buffering on; # 完整上传到 Nginx 后再转后端
# 大文件场景 推荐 off 流式转发 但要求后端能流式接收
}
# 7 内存优化(共享内存)
map_hash_bucket_size 128;
map_hash_max_size 2048;
server_names_hash_max_size 4096;
server_names_hash_bucket_size 256;
types_hash_max_size 2048;
variables_hash_max_size 1024;
}
实战经验:client_max_body_size 默认 1m 上传图片视频必改 否则 413 Request Entity Too Large;large_client_header_buffers 4 16k 必配 否则 JWT 大 token + cookie 必中招 502;proxy_buffering 默认 on 适合普通响应 但 SSE/长轮询/大下载必须关 否则用户等很久才收到第一字节;open_file_cache 静态资源场景能省 30% syscall;client_body_temp_path 务必放快盘或 tmpfs 否则上传慢死;hash 表大小不调 ssl_certificate 多 server_name 启动报 could not build server_names_hash。我们 buffer 调优后 上传成功率从 88% 涨到 99.9% 大下载稳定不再 OOM。
四 压缩、缓存与 CDN 协同
gzip / brotli + proxy_cache + 浏览器缓存 + CDN header 是带宽治理与延迟优化的核心。
http {
# 1 Gzip(老牌)
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5; # 1-9 5 是性价比甜点
gzip_min_length 1024; # 小于 1KB 不压(开销大于收益)
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml
font/woff2;
# 注意 text/html 默认就压 不要再加
# 2 Brotli(新一代 压缩率高 10-25%)
# 需要 ngx_brotli 模块
brotli on;
brotli_comp_level 6;
brotli_min_length 1024;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
brotli_static on; # 预压缩文件直接送
# 3 Proxy cache(动态内容缓存)
proxy_cache_path /var/cache/nginx/proxy
levels=1:2
keys_zone=app_cache:100m
max_size=10g
inactive=60m
use_temp_path=off;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_methods GET HEAD;
proxy_cache_min_uses 2; # 2 次请求才入缓存
}
压缩与 cache zone 准备好之后 还要把缓存策略落到具体 location 上 并区分 API / 静态资源 / 实时数据 / CDN 协同 否则一刀切就会把不该缓的也缓住:
http {
# 4 缓存策略
server {
location /api/articles/ {
proxy_pass http://backend_app;
proxy_cache app_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_lock on; # 同 key 并发请求只让一个穿透
proxy_cache_lock_timeout 5s;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
proxy_cache_background_update on; # 后台异步更新
# 调试 header
add_header X-Cache-Status $upstream_cache_status;
# HIT / MISS / EXPIRED / STALE / UPDATING / REVALIDATED / BYPASS
# 不缓存敏感数据
proxy_no_cache $cookie_session $arg_nocache;
proxy_cache_bypass $cookie_session $arg_nocache;
}
# 5 浏览器缓存(静态资源 hash 文件名)
location ~* \.(jpg|jpeg|png|gif|webp|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header X-Content-Type-Options nosniff;
access_log off; # 静态资源不打日志
}
# 6 短缓存 HTML(配合 hash 资源)
location ~* \.html$ {
expires 5m;
add_header Cache-Control "public, must-revalidate";
}
# 7 强制无缓存(实时数据)
location /api/realtime/ {
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header Pragma "no-cache";
expires -1;
}
}
# 8 CDN 友好 header
server {
# 配合 CloudFront / Cloudflare / 阿里云 CDN
add_header Vary "Accept-Encoding, Accept";
add_header X-Cache-Time $upstream_response_time;
# Stale-while-revalidate(浏览器侧异步更新)
location /api/feeds/ {
add_header Cache-Control "public, max-age=60, stale-while-revalidate=600";
}
}
}
实战经验:gzip_types 默认只压 text/html 必须加上 css/js/json/svg 否则等于没开;brotli 比 gzip 高 10-25% 但需要编译 ngx_brotli 模块 OpenResty 自带;proxy_cache_lock 防止"缓存击穿"同一 key 大并发只让一个回源;proxy_cache_use_stale 是高可用必上 后端挂了还能返回旧缓存;静态资源 access_log off 能省 30% IO;Cache-Control immutable 让浏览器永不重新验证 hash 文件名场景必配;CDN 与 Nginx 协同要靠 Vary header 控制缓存粒度。我们加压缩缓存后 带宽节省 78% 后端 QPS 从 30 万降到 6 万。
[mermaid]
flowchart TD
A[用户请求] --> B[CDN 边缘]
B -->|HIT| C[直接返回]
B -->|MISS| D[Nginx 入口]
D --> E{是静态资源?}
E -->|是| F[本地文件 + 1y 缓存]
E -->|否| G{proxy_cache HIT?}
G -->|HIT| H[直接返回 + X-Cache HIT]
G -->|MISS| I[upstream keepalive 池]
I --> J[后端业务]
J --> K[响应]
K --> L[写 proxy_cache]
L --> M[gzip/brotli 压缩]
M --> N[返回用户]
G -->|STALE+upstream down| O[stale 兜底]
O --> N
五 日志、监控与 SSL/TLS
access log 同步写磁盘在大流量下是 IO 杀手 必须 buffer + flush;SSL 配置不当 P99 延迟翻倍。
http {
# 1 异步日志(关键)
log_format main escape=json
'{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status":$status,'
'"body_bytes":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_time":"$upstream_response_time",'
'"upstream_status":"$upstream_status",'
'"user_agent":"$http_user_agent",'
'"x_forwarded_for":"$http_x_forwarded_for",'
'"request_id":"$request_id"'
'}';
# buffer + flush 异步
access_log /var/log/nginx/access.log main
buffer=64k
flush=5s
gzip=4; # 压缩节省 70% 磁盘
error_log /var/log/nginx/error.log warn;
# 2 条件日志(只记 4xx/5xx 或慢请求)
map $status $loggable {
~^[23] 0; # 2xx 3xx 不记
default 1;
}
map $request_time $slow {
default 0;
~^[5-9]\. 1; # 慢于 5s
~^[1-9][0-9] 1; # 慢于 10s
}
server {
access_log /var/log/nginx/error_only.log main if=$loggable;
access_log /var/log/nginx/slow.log main if=$slow;
}
# 3 静态资源不记日志
location ~* \.(jpg|css|js|woff2)$ {
access_log off;
}
# 4 健康检查不记日志
location = /health {
access_log off;
return 200 "ok";
}
# 5 SSL/TLS 高性能配置
server {
listen 443 ssl http2 reuseport;
server_name example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
# 协议与套件
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# Session 复用(降低握手开销)
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off; # 安全考虑关闭 ticket
# OCSP Stapling(减少客户端验证延迟)
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;
resolver 8.8.8.8 1.1.1.1 valid=300s;
resolver_timeout 5s;
# HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# 安全 header
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
}
# 6 HTTP/2 / HTTP/3
# HTTP/2 默认 ssl + http2
# HTTP/3 (QUIC) Nginx 1.25+ 支持
server {
listen 443 quic reuseport;
listen 443 ssl http2;
http3 on;
ssl_early_data on;
add_header Alt-Svc 'h3=":443"; ma=86400';
}
}
# 7 状态监控(stub_status)
server {
listen 127.0.0.1:8080;
location /nginx_status {
stub_status;
allow 127.0.0.1;
allow 10.0.0.0/8;
deny all;
}
}
# Active connections / accepts / handled / requests / Reading / Writing / Waiting
# 配合 nginx-prometheus-exporter 推 Prometheus
实战经验:access_log buffer=64k flush=5s 必上 否则 30 万 QPS IO wait 50% 卡死;条件日志 map + if 只记 4xx/5xx + 慢请求 日志量降 95% 排查更快;TLS 1.3 + ECDHE + AES-GCM 是黄金组合 兼容性与性能最优;ssl_session_cache 必开 否则每次握手 100-200ms;OCSP Stapling 让客户端不用查 CA 节省一次往返 P99 降 50-150ms;HTTP/2 默认开 HTTP/3 流量重要 30%+ 用户 QUIC 体验大幅提升;stub_status + nginx-prometheus-exporter 是必备监控。我们日志异步后 IO wait 从 50% 降到 3% TLS 握手 P99 从 250ms 降到 20ms。
六 内核调优、Reload 与故障演练
Nginx 跑在 Linux 上 sysctl 不调 Nginx 再快也被内核卡;reload 不当会丢请求 配置错误 30 秒看不到 必须有故障演练。
# 1 内核调优 /etc/sysctl.conf
# 文件描述符
fs.file-max = 2000000
fs.nr_open = 2000000
# TCP 连接队列
net.core.somaxconn = 65535 # listen backlog 上限 默认 128
net.ipv4.tcp_max_syn_backlog = 65535 # SYN 队列
net.core.netdev_max_backlog = 65535 # 网卡入队上限
# TIME_WAIT 优化
net.ipv4.tcp_tw_reuse = 1 # 允许 TIME_WAIT 端口复用
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_max_tw_buckets = 262144
# 端口范围(主动 connect 端口)
net.ipv4.ip_local_port_range = 10240 65535
# TCP buffer(高带宽场景)
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# Keepalive
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
# 防 SYN flood
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 2
# 启用
sysctl -p
# 2 ulimit 设置 /etc/security/limits.conf
* soft nofile 1000000
* hard nofile 1000000
root soft nofile 1000000
root hard nofile 1000000
# systemd 服务额外
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=1000000
内核与 ulimit 是 Nginx 的承载底座 调好之后还有更关键的运维侧:配置验证、reload 零丢连接、平滑升级、graceful shutdown、监控指标 这些直接决定线上能不能不停机演进。下面是日常运维必备的命令清单与故障演练流程:
# 3 Nginx 配置验证(改完先 -t)
nginx -t # syntax check
nginx -T # 输出完整 merged 配置(debug 用)
# 4 平滑 reload(零丢连接)
nginx -s reload
# 原理:
# - master 收到 HUP 信号
# - 重新解析配置
# - 启动新 worker
# - 老 worker 处理完已接收请求后退出
# - 切换期间没有连接丢失
# 5 平滑升级(版本升级零停机)
# 主进程发 USR2 信号 启动新版本 master
kill -USR2 `cat /var/run/nginx.pid`
# 老 master 把 socket 传给新 master
# 新版本验证 OK 后老 master 发 QUIT 退出
kill -QUIT `cat /var/run/nginx.pid.oldbin`
# 失败回滚:老 master 发 HUP 让老 worker 复活 新 master 发 QUIT 退出
# 6 配置故障演练
# 故意改错配置 验证 nginx -t 能检测出来
echo "garbage" >> /etc/nginx/nginx.conf
nginx -t # 应该报错 不要直接 reload
# 7 graceful shutdown
nginx -s quit # 等所有连接处理完再退
# 不要 -s stop(立刻断开所有连接 = 故障)
# 8 监控指标采集
# /opt/nginx_exporter/nginx-prometheus-exporter \
# -nginx.scrape-uri=http://127.0.0.1:8080/nginx_status
# 9 慢请求实时排查
tail -f /var/log/nginx/access.log | jq 'select(.request_time | tonumber > 1)'
# 10 worker 状态查询
ps -ef | grep nginx
# nginx: master process
# nginx: worker process * N
# nginx: cache manager process
# nginx: cache loader process
# 11 fd 占用监控
ls /proc/`pgrep -f "nginx: worker" | head -1`/fd | wc -l
# 单 worker fd 数 接近 worker_connections 说明压力大
# 12 连接状态分布
ss -ant | awk '{print $1}' | sort | uniq -c
# ESTAB / TIME-WAIT / CLOSE-WAIT 各多少
实战经验:somaxconn 默认 128 是大坑 高并发场景必上 65535 否则 listen backlog 满直接 SYN drop;ulimit nofile 必须配 systemd LimitNOFILE 否则 systemd 启动的 nginx 还是默认 1024;nginx -t 是改配置前必跑的安全检查;reload 是真正零丢连接的 配 K8s rolling deploy 简直完美;graceful shutdown 用 quit 不要 stop;监控 fd 数 / 连接状态分布 / 慢请求三件套必上 Grafana。我们做完内核 + Nginx 全套调优后 单机扛 30 万 QPS 零丢包 reload 期间业务无感。
关键概念速查
| 问题 | 关键参数 | 推荐值 | 备注 |
|---|---|---|---|
| worker 进程 | worker_processes | =实际 cpu | auto 在容器里坑 |
| 单 worker 并发 | worker_connections | 65535 | 配合 rlimit_nofile |
| 连接池 | keepalive + http_version 1.1 | 必上 | 少一个就失效 |
| 大上传 | client_max_body_size | 按业务 | 默认 1m 必改 |
| 大 header | large_client_header_buffers | 4 16k | 防 JWT 中招 |
| 压缩 | gzip_types + brotli | 显式列出 | 默认只压 html |
| 缓存 | proxy_cache_lock | 必上 | 防缓存击穿 |
| 日志 | access_log buffer=64k flush=5s | 必上 | IO 杀手 |
| SSL | session_cache + OCSP stapling | 必上 | P99 降 100-200ms |
| 内核 | somaxconn=65535 | 必调 | 默认 128 大坑 |
避坑清单
- 不要 worker_processes auto 在容器里 cgroup 限 4 核它开 64 worker 必崩。
- 不要忘 worker_rlimit_nofile + OS ulimit 双向配 漏一个就限自己。
- 不要 upstream 不配 keepalive 后端 TIME_WAIT 几万个 ulimit 撞死。
- 不要忘 proxy_http_version 1.1 + Connection "" 缺一不可 keepalive 不生效。
- 不要 client_max_body_size 用默认 1m 上传图片视频必 413。
- 不要 gzip 只 on 不配 gzip_types 默认只压 text/html 等于没开。
- 不要 proxy_buffering on 配 SSE/长轮询 用户等很久才收第一字节。
- 不要 access_log 同步写 大流量 IO wait 50% 卡成 PPT 必须 buffer+flush。
- 不要忘 SSL session_cache 每次握手 100-200ms 用户体验差。
- 不要 somaxconn 用默认 128 高并发 SYN drop 灾难。
总结
把 Nginx 性能调优从我们踩过的所有坑里反过来看 你会发现真正能扛超大并发的 Nginx 不是装上就能用 而是一个 worker 模型 + 连接池 + buffer + 压缩缓存 + 日志异步 + SSL/TLS + 内核 sysctl + reload 平滑 + 监控告警的完整工程体系。同一台服务器 同一份 Nginx 用默认配置扛 8 万 QPS 已经 502 一身;调优到位单机 30 万 QPS 稳如老狗 reload 期间业务无感。Nginx 不是"装好改改 server_name 就行"的玩意 它是把 Linux 系统能力榨干的精密机器。
另一个常见的认知误区是把"Nginx 性能好"理解成"装上就稳" 实际上 Nginx 的好性能是建立在大量正确配置之上的 默认配置只是"能跑"的最低门槛 远谈不上"高性能"。真正生产级 Nginx 部署 配置文件至少 500 行 涉及 worker / event / http / server / location / upstream / sysctl 上下游全套优化 每个细节都是用流量峰值下的故障换来的经验。
打个比方 Nginx 性能调优像调一台 F1 赛车。worker 配置是引擎(气缸数与每缸功率配对 多了振动多了少了不够用)连接池是变速箱(挂高挡低转速跑长途 每次起步换挡都是损耗)buffer 是悬挂系统(过小颠路面过大转向迟钝)压缩缓存是空气动力套件(降低风阻 同样动力跑更远)日志异步是 ECU 数据采集(不能拖累引擎性能)SSL 优化是燃油喷射系统(每次握手都是燃料消耗 session 复用就是省油)内核 sysctl 是底盘调校(轮胎气压 / 减震硬度 / 重心高度 整车性能基础)reload 平滑是赛中换胎(几秒搞定不能停赛)。哪一块没调 这辆赛车可能能跑 但要么直道不够快 要么过弯不稳 要么连续作战故障频频。
所以下一次再有人跟你说"Nginx 装上就稳 不用调" 你可以反问他 worker_processes 显式设了吗 worker_connections 与 rlimit 配套了吗 upstream keepalive 上了吗 buffer 按业务调了吗 gzip_types 列全了吗 access_log buffer 了吗 SSL session_cache 开了吗 somaxconn 调了吗 reload 平滑测试做过吗。这些工作没做完 你的 Nginx 只是一个能 hello world 的 demo 不是一个能在 30 万 QPS 下稳定服务的生产级反向代理。从踩坑到投产 中间隔着一整套 Nginx 工程方法论 这条路没有捷径 但走完之后 你的 Nginx 会从"流量峰值就 502 全站崩"变成"老板拍桌叫好 单机扛百万 QPS" 从每次大促心惊胆战变成稳如老狗 从 8 万 QPS 变成 30 万 QPS 真正成为流量入口的定海神针。
—— 别看了 · 2026