- 一、Nginx 处理请求的全流程
- 二、Worker 进程调优
- 2.1 worker_processes — 开几个进程
- 2.2 worker_cpu_affinity — 进程绑核
- 2.3 worker_rlimit_nofile — 文件描述符上限
- 2.4 worker_connections — 单个进程的最大并发连接数
- 三、网络层调优
- 3.1 accept_mutex — 惊群问题
- 3.2 多核负载均衡 — SO_REUSEPORT
- 3.4 tcp_nopush 和 tcp_nodelay
- 3.5 keepalive — 长连接复用
- 3.6 upstream 长连接(重点,很多人忽略)
- 四、缓冲与压缩调优
- 4.1 gzip 压缩
- 4.2 proxy_buffer — 代理缓冲
- 五、静态文件优化
- 5.1 开启文件缓存
- 5.2 静态资源缓存头
- 六、安全相关调优
- 6.1 隐藏版本号
- 6.2 限制请求方法
- 6.3 限流限并发
- 6.4 超时设置防慢速攻击
- 七、日志优化
- 7.1 关掉不必要的访问日志
- 7.2 日志缓冲
- 八、系统内核参数调优
一、Nginx 处理请求的全流程
调优的前提是理解流程。一个 HTTP 请求进来到响应出去,Nginx 经历这些环节:
调优的本质:让每个环节少阻塞、少等待、少浪费。
二、Worker 进程调优
2.1 worker_processes — 开几个进程
# 不要用auto,手动指定更可控
# 规则:CPU几核就开几个,不用多开(多了反而争抢CPU)
worker_processes 4;怎么看CPU核数:nproc 或 lscpu | grep ‘CPU(s)’
2.2 worker_cpu_affinity — 进程绑核
# 4核绑4个进程,一一对应,避免CPU缓存失效
worker_cpu_affinity 0001 00100100 1000;原理:如果worker进程在CPU核心间跳来跳去,L1/L2缓存会失效,性能下降。绑核让每个进程固定在一个核心上跑,缓存命中率高。
2.3 worker_rlimit_nofile — 文件描述符上限
# 每个worker最多打开的文件描述符数
# 计算:worker_connections × 2(一进一出)+ 静态文件数
# 生产环境建议直接给大
worker_rlimit_nofile 65535;同时记得修改系统级限制,否则Nginx配了也没用:
# 临时生效
ulimit -n 65535
# 永久生效,写入 /etc/security/limits.conf
* soft nofile 65535
* hard nofile 655352.4 worker_connections — 单个进程的最大并发连接数
events {
# 单个worker最大并发连接
# 总并发 = worker_processes × worker_connections
# 4 × 65535 = 262140 并发连接
worker_connections 65535;
}注意:这个值不是越大越好。每个连接都占内存,65535连接大约占 65535 × 4KB ≈ 256MB 内存/worker。根据实际内存算。
三、网络层调优
3.1 accept_mutex — 惊群问题
events {
# 高并发场景(worker_connections > 1024)建议关闭
# 关闭后所有worker争抢accept,Linux内核3.9+有REUSEPORT支持,效率更高
# 低并发场景开启,避免空闲唤醒
accept_mutex off;
}什么是惊群:一个新连接来了,所有worker都被唤醒争抢,但只有一个能拿到,其余白跑一趟。accept_mutex on 让worker排队拿连接,避免争抢,但在高并发下排队本身也成了瓶颈。
3.2 多核负载均衡 — SO_REUSEPORT
http {
# 静态文件传输用零拷贝,跳过用户空间
# 数据直接从磁盘到内核缓冲区再到网卡,不走用户态
sendfile on;
}原理对比:
默认:磁盘 → 内核缓冲区 → 用户缓冲区 → socket缓冲区 → 网卡
sendfile:磁盘 → 内核缓冲区 → 网卡 (少拷贝2次)
3.4 tcp_nopush 和 tcp_nodelay
http {
sendfile on;
# 和sendfile配合使用
# 等数据包攒到最大再一次性发出去,减少网络包数量
tcp_nopush on;
# 对于动态内容/小包场景,禁用Nagle算法,有数据立即发
# 和tcp_nopush不冲突:sendfile时用nopush,非sendfile时用nodelay
tcp_nodelay on;
}简单理解:tcp_nopush 是”攒够再发”,适合大文件;tcp_nodelay 是”有就发”,适合API响应。Nginx内部会自动切换。
3.5 keepalive — 长连接复用
http {
# 客户端长连接超时,默认75s,生产建议65s
# 太长占连接,太短频繁握手
keepalive_timeout 65;
# 长连接最大请求数,超过就断开重连
# 防止单个连接占用过久
keepalive_requests 10000;
}3.6 upstream 长连接(重点,很多人忽略)
upstreambackend {
# 保持到后端的长连接池,避免每次请求都重新握手
# 减少Nginx和后端之间的TCP连接开销
keepalive 32;
server 10.0.0.1:8080;
server 10.0.0.2:8080;
}
server {
location /api/ {
proxy_pass http://backend;
# 这三行必须加,否则长连接不生效!
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}为什么重要:默认情况下Nginx到后端是短连接,每个请求都新建TCP连接。高并发下这是巨大的性能浪费。加上这三行后,Nginx和后端之间会复用长连接,QPS提升非常明显。
四、缓冲与压缩调优
4.1 gzip 压缩
http {
gzip on;
gzip_min_length 1k; # 小于1KB不压缩,压缩反而更大
gzip_comp_level 4; # 压缩级别1-9,4是性价比最高的
gzip_types text/plain application/json application/javascript
text/css application/xml text/javascript
application/x-javascript image/svg+xml;
gzip_vary on; # 加Vary头,让CDN正确缓存
gzip_disable "MSIE [1-6]\."; # IE6不支持gzip
}压缩级别选择:
| 级别 | 压缩率 | CPU消耗 |
|---|---|---|
| 1 | 低 | 极低 |
| 4 | 中等 | 适中 |
| 6 | 较高 | 较高 |
| 9 | 最高 | 极高 |
4.2 proxy_buffer — 代理缓冲
http {
# 后端响应的缓冲区设置
proxy_buffering on; # 开启缓冲,Nginx先收完再转给客户端
proxy_buffer_size 4k; # 响应头的缓冲区大小
proxy_buffers 8 16k; # 响应体的缓冲区,8个16KB块
proxy_busy_buffers_size 32k; # 忙时缓冲区大小
# 后端响应慢时,先写到临时文件
proxy_temp_path /tmp/nginx_proxy_temp;
proxy_max_temp_file_size 1024m;
}原理:开了buffering,Nginx先把后端响应吃进缓冲区,然后慢慢喂给慢速客户端。后端可以尽快释放去处理下一个请求。如果关掉,后端必须等客户端收完才能脱身。
五、静态文件优化
5.1 开启文件缓存
http {
# 打开文件描述符缓存,避免重复open/stat系统调用
# 适合大量静态文件的场景
open_file_cache max =10000 inactive=30s;
open_file_cache_valid 60s; # 60s检查一次文件是否变化
open_file_cache_min_uses 2; # 30s内被访问2次以上才缓存
open_file_cache_errors on; # 缓存文件不存在的错误响应
}实测效果:静态文件密集场景,QPS提升 2-3倍。
5.2 静态资源缓存头
server {
location /static/ {
alias /data/www/static/;
# 浏览器缓存30天,期间不会重复请求
expires 30d;
add_header Cache-Control "public, immutable";
# 开启etag,文件变化时浏览器自动刷新缓存
etag on;
}
# HTML文件不缓存,保证用户拿到最新版本
location / {
root /data/www;
expires -1;
add_header Cache-Control "no-cache";
}
}六、安全相关调优
6.1 隐藏版本号
http {
# 隐藏Nginx版本号,防止针对性攻击
server_tokens off;
}6.2 限制请求方法
server {
# 只允许GET、POST和HEAD,其他全拒
if ($request_method !~ ^(GET|POST|HEAD)$ ) {
return 405;
}
}6.3 限流限并发
http {
# 基于IP的请求限流:每秒10个请求,突发允许20个
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# 基于IP的并发连接限制:每个IP最多20个并发
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
location /api/ {
# burst=20 nodelay:突发20个立即处理,不排队
limit_req zone=api_limit burst=20 nodelay;
limit_conn conn_limit 20;
proxy_pass http://backend;
}
}
}$binary_remote_addr vs $remote_addr:二进制格式一个IP只占16字节,文本格式占7-15字节。10MB的共享内存用二进制可以存约60万条记录,文本格式只有一半。
6.4 超时设置防慢速攻击
http {
# 客户端请求头超时
client_header_timeout 15s;
# 客户端请求体超时
client_body_timeout 15s;
# 客户端两次写操作之间的超时
send_timeout 15s;
# 限制请求体大小,防止大文件上传打满内存
client_max_body_size 20m;
# 限制请求头缓冲区大小
client_header_buffer_size 4k;
}七、日志优化
7.1 关掉不必要的访问日志
http {
# 访问日志占大量IO,尤其是SSD场景
# 健康检查、静态资源等可以不记录
# 方法1:直接全局关掉(不推荐,出问题没日志排查)
# access_log off;
# 方法2:条件记录(推荐,生产环境访问日志过多时,可选择配置)
map $status $loggable {
~^[23] 0; # 2xx和3xx不记录
default 1; # 4xx和5xx记录
}
access_log /var/log/nginx/access.log combined if=$loggable;
}
server {
# 静态文件不记日志
location /static/ {
access_log off;
alias /data/www/static/;
}
}7.2 日志缓冲
http {
# 缓冲16KB再写磁盘,减少IO次数
# open_log_file_cache缓存日志文件描述符
access_log /var/log/nginx/access.log combined buffer=16k flush=5s;
open_log_file_cache max=1000 inactive=20s;
}八、系统内核参数调优
Nginx配得再好,系统参数不配合也白搭。以下参数写入 /etc/sysctl.conf:
# 允许TIME_WAIT socket复用,高并发必开
net.ipv4.tcp_tw_reuse =1
# TIME_WAIT最大数量,默认太小
net.ipv4.tcp_max_tw_buckets =65535
# TCP连接队列长度,超过这个值的连接会被丢弃
# 等于 min(somaxconn, 应用backlog) 的较小值
net.core.somaxconn =65535
# SYN队列长度,防SYN Flood
net.ipv4.tcp_max_syn_backlog =65535
# SYN重试次数,默认5次太慢
net.ipv4.tcp_synack_retries =2
# FIN_WAIT2状态超时时间
net.ipv4.tcp_fin_timeout =15
# keepalive探测间隔
net.ipv4.tcp_keepalive_time =600
net.ipv4.tcp_keepalive_intvl =30
net.ipv4.tcp_keepalive_probes =3
# 本地端口范围(临时端口)
net.ipv4.ip_local_port_range =102465535
# TCP读写缓冲区最小/默认/最大值
net.ipv4.tcp_rmem =40968738016777216
net.ipv4.tcp_wmem =40966553616777216
# 网卡收发队列长度
net.core.netdev_max_backlog =65535
# 文件描述符上限
fs.file-max =2000000生效命令:
sysctl -p最后编辑:柳杨 更新时间:2026-06-11 18:29
