
在高并发情况下,Nginx 的性能瓶颈通常不是由CPU或者内存引起的,而是来自于连接管理、负载均衡和数据处理之间的配合效果不好。通过对upstream进行精细化的健康检查、连接池复用以及gzip压缩等设置可以大大减少延迟、提高吞吐量并且增强后端集群的容错能力。
上游的健康状况:由被动的容错转变为积极的感知
传统的被动式健康体检依靠的是请求失败的数量来反映问题,有延迟现象;而主动式的健康体检则是定期对后端的各个节点进行检测,把出现问题的实例提前隔离出来。
upstream backend {
zone backend 64k;
server 10.0.0.1:8080 max_fails=3 fail_timeout=10s;
server 10.0.0.2:8080 max_fails=3 fail_timeout=10s;
# 主动健康检查(需 nginx-plus 或开源版配合 lua/第三方模块)
check interval=3000 rise=2 fall=3 timeout=1000 type=http;
check_http_send "HEAD /health HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}
关键参数解析:
interval:探测间隔,过短增加网络开销,过长降低灵敏度rise/fall:连续成功/失败阈值,避免网络抖动导致的误判zone:共享内存区,实现 worker 进程间状态同步
开源版可以用nginx_upstream_check_module或者Lua脚本来做同样的事情,就是把健康检查的逻辑从请求链中拿出来放到后台去执行。
连接池复用:节省握手时间的秘密武器
在高并发情况下,TCP 连接和 TLS 握手会产生很大的开销,利用 keepalive 连接池来复用的话可以把短连接模式下的握手费用分摊到几乎为零。
upstream backend {
# ... server definitions ...
keepalive 32; # 每个 worker 保持的空闲连接数
keepalive_timeout 60s; # 空闲连接最大存活时间
keepalive_requests 1000; # 单连接最大请求数
}
server {
location /api/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection ""; # 清除 Connection 头,启用 keepalive
}
}
配置要点:
keepalive的值要依据并发量来设定,太大则会造成文件描述符过多而浪费资源,太小则无法起到重复使用的效果proxy_http_version 1.1是前提条件,而 HTTP/1.0 默认不支持长连接proxy_set_header Connection ""是必须的,不然的话 Nginx 就会把客户端的 `Connection: close“
在HTTPS的后端中开启proxy_ssl_session_reuse on;来复用SSL会话以缩短TLS握手的时间。
gzip 压缩:带宽和CPU之间的权衡
压缩可以减小数据量,但是过大的压缩率会导致CPU占用过高,产生新的瓶颈。
http {
gzip on;
gzip_vary on;
gzip_min_length 1024; # 小于 1KB 不压缩,避免膨胀
gzip_comp_level 5; # 压缩级别 1-9,5 为性价比拐点
gzip_proxied any; # 对代理响应也启用压缩
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml; # 仅压缩文本类内容
gzip_disable "msie6"; # 禁用 IE6 的 gzip(兼容性问题)
}
性能权衡原则:
- 压缩等级1到3适用于对CPU比较敏感的场合,5到6是通用的建议值,7到9只有在网络带宽非常紧张的情况下才用
gzip_min_length应该比响应头大一些,否则压缩之后可能会更大- 动态内容的压缩主要是在应用层进行的,而 Nginx 只负责静态资源的处理
使用 Brotli 可以节省 15%-20% 的空间,但是需要编译的时候开启 ngx_brotli 模块并且要考虑浏览器的兼容性问题。
动态负载均衡:运行中热更新
传统的 reload 方式在大范围的上游修改中会出现毫秒级的中断,而使用 Nginx Plus API 或者开源的 lua-upstream-nginx-module 则可以实现在线添加和删除上游节点:
# 动态添加节点示例(Lua)
location /upstream/set {
content_by_lua_block {
local upstream = require "ngx.upstream"
local ok, err = upstream.set_peer(backend, "10.0.0.3:8080", {weight=2, max_fails=3})
if not ok then
ngx.say("failed: ", err)
end
}
}
动态化最重要的价值是:
- 和容器编排系统(如K8s、Consul等)一起工作,可以自动感知到扩容需求
- 灰度发布的时候按照比例进行流量引导,不需要刷新页面
- 故障节点可以秒级删除,在恢复正常之后会自动注入
监控与调优闭环
配置生效之后要进行验证:
# 实时查看 upstream 状态
watch -n 1 'curl -s http://localhost/nginx_status | grep -E "Active|Reading"'
# 连接池命中率估算
ss -tnp | grep nginx | awk '{print $4}' | cut -d: -f1 | sort | uniq -c
关键指标:
keepalive连接复用率:理想状态 > 80%- 健康检查不合格的比例上升了,应该发出警报
- gzip 的压缩比例由
$gzip_ratio来保存,小于1.1表示没有进行有效的压缩
总结
Nginx性能优化的核心就是要在连接复用、负载均衡和压缩之间找到一个最佳的平衡点。健康检查由原来的被动响应改为现在的主动探测,连接池复用把握手成本分摊到了可以忽略不计的程度,gzip压缩则是在带宽和CPU之间找到了性价比最高的地方。三者共同作用再加上动态upstream管理功能就可以形成一个高吞吐量、低延迟以及具有弹性容错能力的流量入口层。