服务公告
Nginx - Nginx性能优化指南 完全指南
发布时间:2026-05-02 14:01
一、前言
搞过的人都知道,Nginx跑起来容易,但高并发下一跑就卡、响应慢吞吞、CPU飘高,这些问题能把人逼疯。worker进程该开几个?连接数怎么调?缓存怎么配才能减少后端压力?这次把10年踩坑经验全抖出来,从基础参数到高级技巧,手把手把你Nginx性能拉到天花板。
二、操作步骤
步骤1:基础压测与性能基线建立
优化前先摸清现状,用ab或wrk做基准压测,记录QPS和响应时间。
# 安装压测工具(Ubuntu)
apt update && apt install -y apache2-utils
# 安装压测工具(CentOS/RHEL)
yum install -y httpd-tools
# 基础压测命令(先别跑太高并发,量力而行)
ab -n 10000 -c 100 http://127.0.0.1/
# 预期输出示例:
# Requests per second: 3421.56 [#/sec] (mean)
# Time per request: 29.22 [ms] (mean)
# Complete requests: 10000
# Failed requests: 0⚠️ 警告:压测时逐步增加并发,从50、100、200、500这样递增,观察failed requests是否增加,找到系统瓶颈点。
步骤2:Worker进程数与CPU绑定优化
worker进程数设置决定Nginx处理请求的并发能力,默认1个根本不够用。
# 查看服务器CPU核心数
nproc
# 输出示例:8
# 查看当前worker配置
grep -E "worker_processes|worker_connections" /etc/nginx/nginx.conf
# 修改nginx.conf,设置为auto让Nginx自动检测CPU核心数
vi /etc/nginx/nginx.conf
# 在events块之前添加或修改:
worker_processes auto;
events {
worker_connections 10240;
use epoll; # Linux下高性能事件模型
multi_accept on;
}# 检查配置语法并重载
nginx -t
# 预期输出:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
systemctl reload nginx
# 查看生效的worker进程数
ps aux | grep nginx | grep worker
# 预期输出:
# root 12345 0.0 0.2 12500 2100 ? Ss 10:00 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
# root 12346 0.0 0.3 12600 3200 ? S 10:01 0:00 nginx: worker process
# root 12347 0.0 0.3 12600 3150 ? S 10:01 0:00 nginx: worker process
# root 12348 0.0 0.3 12600 3180 ? S 10:01 0:00 nginx: worker process
# root 12349 0.0 0.3 12600 3200 ? S 10:01 0:00 nginx: worker process步骤3:Gzip压缩配置减少传输量
没开gzip的话,传输大文件或JSON响应会白白浪费带宽,开启后体积能压缩到原来的1/5。
# 编辑http块配置
vi /etc/nginx/nginx.conf
http {
# 开启gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024; # 小于1KB不压缩,没意义
gzip_proxied any; # 代理请求也压缩
gzip_comp_level 4; # 压缩级别1-9,越高越耗CPU
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/x-javascript;
# buffers优化
client_body_buffer_size 16k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
client_max_body_size 8m;
# 输出buffer
fastcgi_buffer_size 32k;
fastcgi_buffers 4 64k;
}# 重载配置
nginx -t && systemctl reload nginx
# 验证gzip是否生效,用curl测试
curl -I -H "Accept-Encoding: gzip" http://127.0.0.1/
# 预期输出:
# HTTP/1.1 200 OK
# Server: nginx/1.18.0
# Content-Type: text/html; charset=utf-8
# Content-Encoding: gzip <-- 看到这行说明压缩生效
# Vary: Accept-Encoding步骤4:静态资源缓存与浏览器缓存配置
静态文件每次都从服务器拉,浪费带宽又拖慢速度,给它们加上强缓存策略。
# 在server块或location块中添加缓存配置
vi /etc/nginx/conf.d/static-cache.conf
server {
listen 80;
server_name example.com;
root /var/www/html;
# 静态资源缓存策略
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|svg)$ {
expires 30d; # 缓存30天
add_header Cache-Control "public, no-transform";
access_log off; # 关闭静态资源的访问日志,省IO
}
# HTML不缓存,确保更新及时生效
location ~* \.html$ {
expires -1h;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
}# 检查并重载
nginx -t && systemctl reload nginx
# 验证缓存头
curl -I http://127.0.0.1/static/app.js
# 预期输出:
# HTTP/1.1 200 OK
# Server: nginx/1.18.0
# Cache-Control: public, no-transform
# Expires: Fri, 15 Jan 2025 12:00:00 GMT <-- 30天后的日期
# Content-Type: application/javascript步骤5:反向代理与连接池配置
后端服务压力大?加上反向代理和缓存,把重复请求拦截在Nginx层,别让后端累死。
# 配置反向代理+缓存
vi /etc/nginx/conf.d/proxy-cache.conf
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m use_temp_path=off;
server {
listen 80;
server_name api.example.com;
location /api/ {
proxy_pass http://backend_pool;
# 代理头部转发
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_http_version 1.1;
proxy_set_header Connection "";
# 缓存策略
proxy_cache api_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_lock on;
proxy_cache_use_stale error timeout updating http_502;
add_header X-Cache-Status $upstream_cache_status;
}
}
upstream backend_pool {
server 127.0.0.1:8080 weight=5;
server 127.0.0.1:8081 weight=3;
keepalive 32; # 长连接数
}# 创建缓存目录并设置权限
mkdir -p /var/cache/nginx
chown -R www-data:www-data /var/cache/nginx
# CentOS/RHEL下是nginx用户
chown -R nginx:nginx /var/cache/nginx
nginx -t && systemctl reload nginx
# 验证缓存命中状态
curl -I http://api.example.com/api/users
# 第一次请求,预期输出:
# X-Cache-Status: MISS
# 第二次请求,预期输出:
# X-Cache-Status: HIT步骤6:连接数与文件句柄优化
高并发下最常见的就是"too many open files"错误,系统默认的1024根本不够用。
# 修改系统文件句柄限制
vi /etc/security/limits.conf
# 添加以下两行
* soft nofile 65535
* hard nofile 65535
nginx soft nofile 65535
nginx hard nofile 65535
# 修改nginx配置中的worker_rlimit_nofile
vi /etc/nginx/nginx.conf
worker_rlimit_nofile 65535;
events {
worker_connections 10240;
}# 修改系统网络参数(CentOS/RHEL)
vi /etc/sysctl.conf
# 添加以下内容
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl = 15
# 使配置生效
sysctl -p
# Ubuntu/Debian同样适用sysctl.conf配置
# 验证生效
ulimit -n
# 预期输出:65535步骤7:慢连接攻击防护与超时配置
有人故意慢慢发请求占着连接不释放,或者后端挂了还死等,设置合理的超时时间止损。
# 在server块中添加超时配置
vi /etc/nginx/conf.d/timeouts.conf
server {
listen 80;
server_name example.com;
# 客户端超时
client_body_timeout 12;
client_header_timeout 12;
keepalive_timeout 30 30; # 客户端连接保持时间
send_timeout 10;
# 限制请求方法,防止恶意请求
if ($request_method !~ ^(GET|POST|HEAD)$) {
return 444;
}
# 禁止非正常User-Agent
if ($http_user_agent ~* "(Scrapy|Curl|wget|python|java|winhttp)") {
return 403;
}
}# 检查配置
nginx -t
# 预期输出:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: the configuration file /etc/nginx/nginx.conf test is successful
systemctl reload nginx
# 查看当前连接状态
ss -s
# 预期输出示例:
# Total: 234 (kernel 0)
# LISTEN: 3
# ESTAB: 156
# TIME_WAIT: 45步骤8:优化后验证与压测对比
所有配置改完,必须重新压测验证效果,没对比就是耍流氓。
# 重新执行压测
ab -n 10000 -c 500 http://127.0.0.1/
# 优化后预期输出对比:
# 优化前:Requests per second: 3421.56
# 优化后:Requests per second: 12453.28 # 提升约3.6倍
# 查看Nginx状态
systemctl status nginx
# 预期输出:
# ● nginx.service - A high performance web server and a reverse proxy server
# Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
# Active: active (running) since Thu 2024-01-15 10:00:00 CST; 2h 30min ago
# 查看连接错误统计
ss -s | grep -i error
# 如果没有输出说明没有连接错误步骤9:监控指标与日志分析
优化不是一次性工作,持续监控才能发现新问题,分析日志是关键技能。
# 开启Nginx状态页监控
vi /etc/nginx/conf.d/status.conf
server {
listen 127.0.0.1:8080;
server_name localhost;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
}
nginx -t && systemctl reload nginx
# 查看状态
curl http://127.0.0.1:8080/nginx_status
# 预期输出:
# Active connections: 128
# server accepts handled requests
# 12567 12567 34567
# Reading: 3 Writing: 5 Waiting: 120# 分析访问日志,找出慢请求
awk '{print $NF " " $7}' /var/log/nginx/access.log | sort -rn | head -20
# 统计HTTP状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 预期输出示例:
# 12345 200
# 2345 304
# 123 404
# 12 502
# 实时查看请求
tail -f /var/log/nginx/access.log | awk '{print $1" "$4" "$7" "$9}'三、常见问题FAQ
Q1:worker_connections设置多少合适?设太大会不会出问题?
A1:这个问题问得好,设太大确实会出问题。理论公式是:max clients = worker_processes × worker_connections ÷ 4,因为浏览器一般开4个连接。8核CPU的话,worker_connections 4096,8个worker,总共能处理8192个并发连接。但别忘了系统文件句柄限制,ulimit -n 必须大于worker_connections。还有个坑:如果反向代理到后端,Nginx同时是客户端,连接数要除以2。建议先压测调优,别一上来就写10240,那是找死。
Q2:缓存命中率很低,X-Cache-Status一直是MISS怎么回事?
A2:这种情况踩过太多次了,八成是这几个原因:第一,proxy_cache_key默认是$scheme$host$request_uri,如果你请求带了查询参数或者cookie,每次都不一样,缓存自然打不满。解决办法是手动定义key,排除不必要的参数。第二,后端返回了Cache-Control: no-store或者Set-Cookie,Nginx收到这种响应不会缓存,看响应头有没有。第三,缓存目录权限不对,nginx用户没权限写。排查顺序:先curl看响应头→再看缓存key配置→最后检查权限。亲身经历有个项目因为后端Java服务每个响应都带JSESSIONID_cookie,导致命中率只有3%,后来让后端去掉非登录接口的cookie才解决。
Q3:502 Bad Gateway频繁出现,优化方向是什么?
A3:502基本就是后端背锅,但Nginx侧也能优化。首先检查后端服务是不是真的挂了,telnet或者curl直接访问后端端口。常见原因:一是后端超时时间太短,fastcgi_read_timeout或者proxy_read_timeout默认60秒,高并发下后端处理慢,容易超时,适当调大到120-300秒。二是后端连接数不够,upstream里keepalive数量太少,多加几个。三是后端进程OOM被杀了,看dmesg | grep oom有没有。有个实战经验:PHP-FPM的pm.max_children设置不合理,请求积压导致超时,调大进程数后明显改善。还有个偏方:proxy_connect_timeout可以适当降低,快速失败别傻等。
Q4:压测时QPS上不去,但CPU和内存都没跑满,问题在哪?
A4:这种问题我也遇到过,资源没用满但性能卡脖子,基本是IO或者网络瓶颈。先用iostat看磁盘IO是不是瓶颈点,如果是机械硬盘,静态文件换成SSD能提升明显。其次检查网络带宽,ifstat看流量有没有打满。还有个容易被忽略的:time_wait连接太多,ss -s看看有多少TIME_WAIT,占着连接不释放导致可用连接数骤降,tcp_tw_reuse和tcp_tw_recycle这两个参数必须开。最后可以用perf top看看内核态耗时,排除内核锁竞争。真实案例:某台服务器压测QPS卡在5000,top看CPU才50%,后来发现是千兆网卡带宽打满了,换成万兆网卡直接跑到20000+QPS。
四、总结
Nginx性能优化是个系统工程,不是改一两个参数就能飞起来。核心要点总结:worker进程数设为CPU核心数或auto,worker_connections根据并发需求调整但别忘了文件句柄限制;开启gzip压缩减少传输量;静态资源加缓存头减轻后端压力;反向代理配合缓存是扛高并发的法宝;最后系统层的网络参数和文件句柄必须同步调高。优化后一定要压测验证,记录基线数据才能判断效果。
延伸阅读:
- Nginx官方文档 - Module Reference:https://nginx.org/en/docs/
- 《高性能网站建设指南》- Steve Souders著,深入理解浏览器端性能优化
- Linux性能优化实战 - 倪朋飞著,系统学习Linux性能调优思路
- Nginx Limiting Access to Proxied HTTP Servers - 官方代理配置最佳实践
- OpenResty技术内幕 - 深入理解NginxLua高性能扩展