性能调优与高并发
性能调优与高并发
写在前面
前几篇讲了反向代理、缓存、安全。这一篇是性能调优——怎么让单机 Nginx 处理更多并发、更短延迟。本篇会从 Nginx 自身配置到 Linux 内核参数,逐层调优,最后实战把单机 Nginx 从 1w 并发提到 10w 并发的真实过程。
一、性能调优的基本思路
性能问题分两类:
- 延迟高:单次请求慢——查后端、磁盘 IO、计算
- 吞吐低:并发上不去——资源瓶颈、配置瓶颈
本篇主要讲**吞吐(并发能力)**的调优。
1.1 性能基准
调优前先测基线——不调优时能跑多少并发?
# 用 wrk
wrk -t8 -c1000 -d30s https://api.company.com/health
# Running 30s test @ https://api.company.com/health
# 8 threads and 1000 connections
# Thread Stats Avg Stdev Max +/- Stdev
# Latency 45.21ms 12.34ms 200.32ms 89.21%
# Req/Sec 2.78k 345.21 3.45k 78.32%
# 224567 requests in 30.10s, 156.34MB read
# Requests/sec: 7462.32
# Transfer/sec: 5.19MB
基线:~7500 QPS,P99 延迟 80ms。
二、Nginx 自身配置调优
2.1 worker 数量
worker_processes auto; # = CPU 核心数
默认 auto,已经是 CPU 核心数——一般不用动。
但不是 worker 越多越好——worker 多了 CPU 调度开销也大。通常 worker 数 = CPU 核心数,或最多 2 倍。
# 看 CPU 核心数
nproc
# 8
# worker 数应该 = 8
worker_processes 8;
2.2 worker 连接数
events {
worker_connections 4096; # 每个 worker 的最大连接数
}
最大并发 = worker_processes × worker_connections。
8 worker × 4096 = 32768 并发连接。
但理论不等于实际——每个反向代理连接消耗 2 个 fd(client + upstream),所以实际可服务的客户端连接 ≈ 16000。
2.3 多进程 accept
events {
accept_mutex off; # 多进程同时 accept,提高吞吐
multi_accept on; # 一次 accept 所有等待连接
use epoll; # Linux 必用 epoll
}
accept_mutex off 适合高并发场景,on 适合连接数少但每个连接负载重的场景。
2.4 worker_cpu_affinity
绑定 worker 到固定 CPU,避免 CPU 缓存失效:
worker_cpu_affinity auto; # 自动绑定
# 或者手动(8 核机器)
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
⚠️ 不是所有场景都需要——某些 CPU 架构(比如 ARM big.LITTLE)手动绑定反而更差。
2.5 worker_priority
worker_priority -5; # nice 值,默认 0
降低 worker 优先级,保证系统响应(SSH、其他进程不被饿死)。
三、零拷贝与文件传输
3.1 sendfile
sendfile on;
Linux 内核的 sendfile(2) 系统调用,内核态直接传输文件——跳过用户态的中转。
对静态文件(图片、CSS、JS)性能提升巨大。
3.2 tcp_nopush
tcp_nopush on;
等数据包累积到 MSS 再发送,减少网络包数量。必须和 sendfile 配合。
3.3 aio(异步 IO)
location /video/ {
aio on;
directio 512k; # 大于 512K 用 O_DIRECT,绕过 page cache
output_buffers 2 1m;
}
异步 IO 读取大文件——适合视频下载、文件下载服务。
3.4 thread pools
# 编译时带 --with-threads
thread_pool default threads=32 max_queue=65536;
location /download/ {
aio threads;
# 异步 IO 用 thread pool 处理,worker 不阻塞
}
磁盘 IO 阻塞 worker 是高并发杀手。thread pool 把磁盘 IO 异步化,worker 不会卡住。
四、长连接优化
4.1 客户端 keep-alive
http {
keepalive_timeout 65;
keepalive_requests 1000;
}
客户端到 Nginx 的长连接。生产推荐 65s——足够复用,但又不浪费 fd。
4.2 upstream keep-alive
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
keepalive 32; # 每个 worker 保持 32 个连接到 upstream
keepalive_requests 1000;
keepalive_timeout 60s;
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1; # 必须 HTTP/1.1
proxy_set_header Connection ""; # 清除 Connection: close
}
}
Nginx 到 upstream 的长连接——避免每次请求都新建 TCP 连接。
坑:默认 Connection: close,即使你写了 proxy_http_version 1.1。必须显式 proxy_set_header Connection ""。
实测:启用 upstream keep-alive,QPS 提升 30%+。
4.3 HTTP/2
listen 443 ssl http2;
HTTP/2 多路复用,单连接并行多个请求。必须 HTTPS。
五、buffer 调优
5.1 客户端请求
http {
client_max_body_size 20m; # 上传文件大小限制
client_body_buffer_size 128k; # 第一个 buffer 大小
client_header_buffer_size 4k; # header buffer
large_client_header_buffers 4 8k; # 大 header buffer
}
5.2 代理缓冲
location / {
proxy_pass http://backend;
proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
proxy_max_temp_file_size 1024m;
}
proxy_buffering on 把响应全部收下来再发给客户端——适合客户端网络慢的场景。
5.3 流式响应
location /sse/ {
proxy_pass http://backend;
proxy_buffering off; # 实时传输
proxy_cache off;
chunked_transfer_encoding on;
}
适合 SSE、长轮询、WebSocket。
六、Linux 内核参数调优
Nginx 的连接数上限受系统 fd 限制和内核参数影响。
6.1 文件描述符限制
# 系统级
fs.file-max = 1000000
# 用户级(/etc/security/limits.conf)
* soft nofile 1000000
* hard nofile 1000000
# Nginx 配置
worker_rlimit_nofile 1000000;
6.2 TCP 参数
# /etc/sysctl.conf
# TCP 连接队列
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# TIME_WAIT 复用
net.ipv4.tcp_tw_reuse = 1
# TIME_WAIT 数量上限
net.ipv4.tcp_max_tw_buckets = 5000
# TCP 缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# 端口范围
net.ipv4.ip_local_port_range = 1024 65535
# TCP keepalive
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
应用:
sudo sysctl -p
6.3 网络设备参数
# 队列长度
net.core.netdev_max_backlog = 65535
# 巨型帧
net.ipv4.tcp_mtu_probing = 1
6.4 连接跟踪
如果用 iptables:
net.netfilter.nf_conntrack_max = 1000000
net.nf_conntrack_max = 1000000
七、磁盘 IO 优化
7.1 IO 调度器
# 看当前调度器
cat /sys/block/vda/queue/scheduler
# [none] mq-deadline kyber bfq
# SSD 用 none,机械盘用 deadline
echo none > /sys/block/vda/queue/scheduler
7.2 文件系统挂载选项
# /etc/fstab
/dev/vda1 /var/log ext4 defaults,noatime,nodiratime 0 0
noatime:不更新文件访问时间nodiratime:目录也不更新
减少磁盘写入。
7.3 缓存目录用 tmpfs
# /etc/fstab
tmpfs /var/cache/nginx tmpfs defaults,size=2G 0 0
proxy_cache 目录用 tmpfs——重启会丢,但 Nginx 重建缓存很快。
八、实战:单机从 1w 到 10w 并发
8.1 测试环境
- CPU: 8 核 Intel Xeon
- 内存: 16GB
- 网卡: 10Gbps
- 后端: 单 Node 服务(响应时间 50ms)
8.2 调优前
QPS: 7500
P99: 80ms
CPU: 60% (4 核满载)
8.3 第一轮:Nginx 基础调优
worker_processes 8;
worker_rlimit_nofile 1000000;
events {
worker_connections 4096;
multi_accept on;
accept_mutex off;
use epoll;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 1000;
upstream backend {
server 10.0.0.1:3000;
keepalive 64;
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
QPS: 12000 (+60%)
P99: 65ms (-19%)
CPU: 75%
8.4 第二轮:内核参数调优
sysctl -w net.core.somaxconn=65535
sysctl -w net.ipv4.tcp_max_syn_backlog=65535
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"
QPS: 18000 (+50%)
P99: 55ms
CPU: 85%
8.5 第三轮:fd 限制 + worker 连接数
# /etc/security/limits.conf
* soft nofile 1000000
* hard nofile 1000000
worker_rlimit_nofile 1000000;
events {
worker_connections 65535; # 从 4096 提高
}
QPS: 28000 (+56%)
P99: 50ms
8.6 第四轮:开启 HTTP/2
listen 443 ssl http2;
浏览器并发连接数从 6 提到 100+。
QPS: 35000
P99: 45ms
8.7 第五轮:thread pool 处理磁盘 IO
thread_pool disk_pool threads=16 max_queue=1024;
location /static/ {
sendfile on;
aio threads=disk_pool;
directio 512k;
}
把磁盘 IO 异步化,避免 worker 阻塞。
QPS: 42000 (+20%)
8.8 第六轮:多 Nginx 实例 + CPU 绑定
如果还是不够,可以跑多个 Nginx 实例绑不同 CPU:
# 实例 1 - CPU 0-3
taskset -c 0-3 nginx -c /etc/nginx/nginx-1.conf
# 实例 2 - CPU 4-7
taskset -c 4-7 nginx -c /etc/nginx/nginx-2.conf
或者用 HAProxy 在前面分发。
8.9 最终结果
QPS: 100000+
P99: 50ms
CPU: 95% (8 核)
内存: 4GB
从 7500 QPS 到 10w QPS,提升 13 倍——全是配置和内核调优,没有一行代码优化。
九、压测工具
9.1 wrk
# 基础压测
wrk -t8 -c1000 -d30s https://api.company.com/health
# 复杂场景用 Lua 脚本
wrk -t8 -c1000 -d30s -s post.lua https://api.company.com/api/orders
9.2 ab(Apache Bench)
ab -n 10000 -c 1000 https://api.company.com/health
简单但功能有限。
9.3 vegeta
echo "GET https://api.company.com/health" | vegeta attack -rate=1000 -duration=30s | vegeta report
支持更复杂的报告格式。
十、本篇小结
| 调优点 | 关键指令 |
|---|---|
| Worker 数量 | worker_processes auto |
| Worker 连接 | worker_connections 65535 |
| 文件传输 | sendfile on + tcp_nopush on |
| 异步 IO | aio threads + thread_pool |
| 长连接 | upstream keepalive + Connection "" |
| 缓冲 | proxy_buffering + buffer 调优 |
| 内核 | somaxconn + tcp_tw_reuse |
| fd 限制 | worker_rlimit_nofile + ulimit |
下一篇讲 常见 502/504 故障排查,把生产中遇到的所有 5xx 故障做一份速查表。