服务公告

服务公告 > 综合新闻 > Nginx - Nginx性能优化指南 完全指南

Nginx - Nginx性能优化指南 完全指南

发布时间:2026-05-02 14:01
Nginx性能优化:反向代理与缓存配置实战指南

一、前言

搞过的人都知道,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高性能扩展