引言:从500ms到50ms,不是神话是工程
500ms 的响应时间对用户来说已经是明显可感知的延迟——页面加载转圈、按钮点击后要等半秒才有反馈、API 超时风险随时触发。如果是核心接口,这个延迟会直接影响用户体验和业务转化率。
但 500ms 到 50ms 的优化不是靠运气,不是靠玄学,是靠一层层排查、一项项配置调出来的结果。Nginx 作为反向代理和前端服务器,是大多数 Web 服务的入口,它对响应时间的影响是全链路的:从连接建立、请求转发、后端响应、内容压缩、静态资源缓存到 keepalive 复用,每一层都有优化空间。
本文用一个真实的优化案例,从问题定位→分层排查→配置调优→验证复盘的完整闭环出发,展示如何将 Nginx 响应时间从 500ms 压到 50ms。每一步都有可执行的命令和可测量的结果。
前提说明:本文的优化路径基于真实场景,但 Nginx 版本差异、操作系统配置、后端业务特性都会影响优化效果。有些参数不是越大越好,也不是所有优化都适用于所有场景。优化前先做基线测量,优化后对比效果,有效果则保留,无效果则回滚。
第一阶段:建立基线——先测量,再优化
问题背景
运维团队收到反馈:用户访问商品详情页感觉很慢,开发排查后怀疑是 Nginx 这一层的问题,丢给运维处理。这是一个典型的"页面加载慢"的投诉,500ms 响应时间是 SLA 告警阈值。
现象:用户点击商品详情页,浏览器转圈约 0.5 秒才能看到内容。
排查前的准备工作
第一步:确认测试环境
不要直接在生产机做测试。先用测试环境或 staging 环境做验证,确认优化有效后再灰度到生产。
# 确认 Nginx 版本(不同版本配置项有差异) nginx -v # nginx version: nginx/1.24.0 # 确认 Nginx 编译参数(了解已加载的模块) nginx -V # 重点关注:--with-http_gzip_static_module、--with-http_ssl_module、 # --with-http_stub_status_module、--with-http_v2_module # 确认操作系统版本 cat /etc/os-release # NAME="Ubuntu 22.04.3 LTS" # VERSION_ID="22.04" # 确认 CPU 核心数(影响 worker 进程数配置) nproc # 8 # 确认内存大小(影响缓存和连接数配置) free -h # Mem: 31Gi
第二步:找到慢请求的日志
Nginx 的 access log 默认记录了每个请求的响应时间:
# 查看 access log 格式(确认响应时间字段) grep -E"log_format|access_log"/etc/nginx/nginx.conf # access_log 示例格式: # log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for" ' # 'rt=$request_time uct="$upstream_connect_time" ' # 'uht="$upstream_header_time" urt="$upstream_response_time"'; # rt 是总响应时间(request_time) # uct 是与后端建立连接的时间 # uht 是后端返回 header 的时间 # urt 是后端返回完整响应的时间
找到rt(总响应时间)超过 500ms 的请求:
# 提取 access log 中响应时间超过 500ms 的请求
awk'{if ($NF > 0.5) print $0}'/var/log/nginx/access.log | tail -50
# 统计响应时间分布
awk'{print $NF}'/var/log/nginx/access.log |
awk -F.'{print $1}'| sort | uniq -c | sort -rn | head -20
# 输出示例:
# 8500 0 (0ms以下)
# 1200 0 (0-0.1s)
# 400 1 (1s)
# 150 2 (2s)
# 80 5 (5s+)
第三步:用 curl 模拟请求,获取详细时间分解
# -w 参数输出各阶段耗时,-o /dev/null 不输出响应体
curl -o /dev/null -s -w"
DNS Lookup: %{time_namelookup}s
TCP Connect: %{time_connect}s
SSL Handshake: %{time_appconnect}s
TTFB (First Byte): %{time_starttransfer}s
Total Time: %{time_total}s
"https://api.example.com/product/detail?id=12345
# 输出示例:
# DNS Lookup: 0.005s
# TCP Connect: 0.012s
# SSL Handshake: 0.045s
# TTFB (First Byte): 0.487s ← 主要时间消耗在这里
# Total Time: 0.512s
关键发现:TTFB(Time To First Byte,首字节时间)高达 487ms,占总时间的 95%。这说明瓶颈在后端响应或 Nginx 与后端之间的网络延迟,而不是 Nginx 本身。
第四步:区分瓶颈在哪一层
# 测试 Nginx 本身处理静态文件的速度(排除后端影响) time curl -o /dev/null -s http://127.0.0.1/static/product.js # 输出:直接返回静态文件,总时间应该在 5ms 以内 # 如果超过 10ms,说明 Nginx 配置有问题或磁盘 I/O 是瓶颈 # 测试本地回环网络延迟(排除公网影响) ping 127.0.0.1 # 正常应该在 0.01ms 以内 # 测试后端服务响应时间(直接 curl 后端,不走 Nginx) time curl -o /dev/null -s http://127.0.0.1:8080/api/product/detail?id=12345 # 如果直接调后端只需要 50ms,说明问题在 Nginx 层 # 如果直接调后端也需要 400ms,说明问题在后端服务本身
基线测量总结
| 指标 | 测量值 | 目标值 |
|---|---|---|
| Nginx 总响应时间 | 512ms | <50ms |
| TTFB(首字节时间) | 487ms | <30ms |
| Nginx 处理静态文件 | 3ms | <5ms |
| 后端 API 响应时间 | 485ms | <30ms |
结论:瓶颈在后端 API 响应时间,Nginx 作为反向代理需要从减少转发延迟、启用缓存、优化连接复用等方面入手。同时后端服务本身也需要优化。
第二阶段:Nginx 层优化——连接、缓存、压缩
优化1:启用 upstream keepalive,减少连接建立开销
Nginx 与后端之间每次请求都新建 TCP 连接,这个过程在 Linux 内核里需要三次握手,Nginx 层面还需要 upstream connect time。如果后端在异地机房,一次连接建立可能就是几十毫秒。
问题现象:$upstream_connect_time居高不下
# 查看当前 upstream 连接时间分布
awk -F'"''{print $NF}'/var/log/nginx/access.log |
awk'{print $NF}'| awk -F.'{print $1}'| sort | uniq -c | sort -rn | head -10
优化前配置(/etc/nginx/conf.d/upstream.conf):
upstream backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}
优化后配置:
upstream backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
keepalive 32; # 保持的空闲连接数
keepalive_requests 1000; # 每个连接最多处理多少请求后关闭
keepalive_timeout 60s; # 空闲连接保持多久
}
然后在对应的server块里配置proxy_http_version 1.1和proxy_set_header Connection "":
server {
listen 443 ssl http2;
server_name api.example.com;
location /api/ {
proxy_pass http://backend;
proxy_http_version 1.1; # 启用 HTTP/1.1
proxy_set_header Connection ""; # 清空 Connection 头,开启 keepalive
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_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# Buffer 配置(减少后端压力)
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
}
}
验证方式:
# 重新加载 Nginx 配置
sudo nginx -t && sudo nginx -s reload
# 再次用 curl 测量
curl -o /dev/null -s -w"
Upstream Connect: %{time_connect}s
TTFB: %{time_starttransfer}s
Total: %{time_total}s
"https://api.example.com/product/detail?id=12345
# 对比优化前后的 upstream_connect_time
# 预期:从 10-30ms 降低到 1-5ms
效果预估:减少连接建立开销,响应时间可降低 20-50ms。
优化2:开启 gzip 压缩,减少传输体积
传输体积大是 TTFB 高的另一个原因——即使后端响应快,内容从后端传到 Nginx、从 Nginx 传到用户,中间传输耗时跟体积成正比。
http {
gzip on;
gzip_vary on; # 对代理请求也启用压缩
gzip_proxied any; # 对代理请求也启用压缩
gzip_comp_level 4; # 压缩级别 1-9,默认 4,平衡速度与压缩率
gzip_min_length 256; # 小于 256 字节不压缩(压缩头部开销不划算)
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml
application/xml+rss
application/x-javascript
image/svg+xml;
gzip_disable "msie6"; # 禁用 IE6 的 gzip(现代浏览器已不需要)
}
验证方式:
# 测试是否有 gzip 压缩(看响应头) curl -I -H"Accept-Encoding: gzip"https://api.example.com/product/detail?id=12345 # 输出中应该有: # Content-Encoding: gzip # Content-Length: 1234 ← 压缩后体积 # 对比压缩前后的 Content-Length curl -I https://api.example.com/product/detail?id=12345 # Content-Length: 5678 ← 原始体积
效果预估:JSON 响应压缩率通常在 60-80%,传输时间可降低 30-60%。
优化3:启用 proxy_cache,将频繁访问的响应缓存住
后端响应慢,但相同请求的响应内容可能是一样的(比如商品详情、分类列表)。把这些缓存到 Nginx 本地,后续相同请求直接返回缓存,不用每次都打到后端。
http {
# 定义缓存路径和参数
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m
max_size=1g inactive=60m use_temp_path=off;
# 缓存响应码 200 和 304,保留 10 分钟
proxy_cache_valid 200 304 10m;
proxy_cache_valid 404 1m; # 404 也缓存 1 分钟,避免频繁回源
# 缓存 Key:按 host + uri + query string
proxy_cache_key "$scheme$request_method$host$request_uri";
# 响应头添加缓存状态(方便调试)
add_header X-Cache-Status $upstream_cache_status;
}
server {
location /api/ {
proxy_pass http://backend;
# 启用缓存(默认 off)
proxy_cache api_cache;
# 遇到这些请求头时,不走缓存(强制回源)
proxy_cache_bypass $cookie_nocache $arg_nocache $arg_comment;
# 同样的,多久算旧缓存需要重新验证
proxy_cache_revalidate on;
# 允许在后台更新缓存(避免缓存过期瞬间大量请求打到后端)
proxy_cache_background_update on;
proxy_cache_lock on;
# 缓存 Key 其他部分保持不变...
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
}
}
验证方式:
# 第一次请求(MISS,表示缓存未命中,回源)
curl -I https://api.example.com/product/detail?id=12345
# X-Cache-Status: MISS
# 第二次请求(HIT,表示命中缓存)
curl -I https://api.example.com/product/detail?id=12345
# X-Cache-Status: HIT
# 等 10 分钟后再次请求(EXPIRED,缓存过期)
curl -I https://api.example.com/product/detail?id=12345
# X-Cache-Status: EXPIRED
# 查看缓存目录大小
du -sh /var/cache/nginx
# 查看缓存命中率
awk'{print $NF}'/var/log/nginx/access.log |
grep -E"HIT|MISS|EXPIRED"| sort | uniq -c
效果预估:缓存命中时,Nginx 直接返回缓存,响应时间可从 500ms 降到 5-10ms。
优化4:调整 worker 进程数和连接数上限
Nginx 默认的 worker 进程数可能不是最优的。在多核服务器上,如果 worker 数太少,无法充分利用 CPU;如果 worker 数太多,上下文切换开销又会成为瓶颈。
# 当前 Nginx worker 进程数配置 grep -E"worker_processes|worker_connections"/etc/nginx/nginx.conf
优化配置:
# /etc/nginx/nginx.conf
worker_processes auto; # 自动等于 CPU 核心数(推荐),也可以手动指定数字
# 每个 worker 进程允许的最大连接数(不要超过 1024 的太多倍)
worker_rlimit_nofile 65535;
events {
worker_connections 4096; # 单个 worker 最大连接数
multi_accept on; # 一次接受多个新连接(提高并发能力)
use epoll; # Linux 下使用 epoll(高效事件驱动模型)
}
同时修改系统的文件描述符上限:
# 查看当前 limit ulimit-n # 临时生效(当前 session) ulimit-n 65535 # 永久生效(/etc/security/limits.conf) # 添加: # * soft nofile 65535 # * hard nofile 65535
验证方式:
# 确认 worker 进程数等于 CPU 核心数 ps aux | grep nginx | grep worker # 应该看到 8 个 worker 进程(假设 8 核) # 确认 worker_connections 生效(需要 reload 后) sudo nginx -t && sudo nginx -s reload # 用 ab 或 wrk 做压力测试,观察延迟和吞吐量 # ab(Apache Benchmark) ab -n 1000 -c 100 https://api.example.com/product/detail?id=12345 # wrk(更现代的压测工具) wrk -t4 -c100 -d30s https://api.example.com/product/detail?id=12345
优化5:开启 HTTP/2,减少请求排队
HTTP/1.1 下,浏览器对同一个域名的并发请求数有限制(通常 6-8 个),超过的部分需要排队。HTTP/2 的多路复用(Multiplexing)允许在同一个 TCP 连接上并行传输多个请求,彻底消除队头阻塞。
server {
listen 443 ssl http2; # 加上 http2 参数即可启用 HTTP/2
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# HTTP/2 相关优化
http2_idle_timeout 60s;
http2_max_concurrent_streams 128; # 单连接最大并发流数
}
验证方式:
# 用 curl 验证是否启用 HTTP/2 curl -I --http2 https://api.example.com/ # 输出应该包含: # HTTP/2 200 # 如果返回 HTTP/1.1,说明 HTTP/2 没有启用 # 用 Chrome DevTools 查看请求的协议版本 # Network 面板 → Protocol 列会显示 h2(HTTP/2)
第三阶段:后端服务优化——不只是 Nginx 的事
优化6:后端连接池大小调优
即使 Nginx 这边用了 keepalive,如果后端服务的连接池配置不合理,还是会瓶颈。Java 服务的连接池(HikariCP、Druid)、Python 的 DBPool、Go 的 sql.DB——每个后端服务的连接池都有最大连接数限制。
# 查看后端服务的连接池状态(假设后端是 Java,用 JDBC) # 通过 JMX 或 actuator 端点查看活跃连接数 curl -s http://127.0.0.1:8080/actuator/metrics/hikaricp.connections.active # 如果活跃连接数接近最大连接数,说明连接池是瓶颈 # 需要增加最大连接数或优化慢查询 # 查看 MySQL 的连接数 mysql -e"SHOW STATUS LIKE 'Threads_connected'; SHOW VARIABLES LIKE 'max_connections';"
优化7:后端启用 Redis 缓存,减少数据库查询
商品详情的响应如果有 400ms,其中可能有 300ms 花在数据库查询上。后端缓存(Redis/Memcached)是最有效的优化手段。
# 查看慢查询日志(MySQL) mysql -e"SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 0.5;" mysql -e"SHOW VARIABLES LIKE 'slow_query_log%';" tail -f /var/log/mysql/slow-query.log # 典型的慢查询:缺少索引、SELECT *、大结果集 # 例如: # SELECT * FROM products WHERE id = 12345; ← 没有索引 # SELECT * FROM products WHERE category_id IN (1,2,3,4,5); ← 大结果集
第四阶段:系统层优化——别让 OS 成为瓶颈
优化8:调整内核参数,减少网络延迟
Linux 内核的网络参数默认值是为通用场景设计的,在高并发短连接场景下,这些默认值可能导致性能问题。
# /etc/sysctl.conf 添加以下配置 # 减少 TIME_WAIT 状态的连接占用(高并发短连接场景) net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 15 # 增加本地端口范围(高并发场景避免端口耗尽) net.ipv4.ip_local_port_range = 32768 60999 # 增加最大文件描述符(每个连接占用一个 fd) fs.file-max = 2097152 # 增大 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 # 启用 TCP 快速打开(减少三次握手延迟) net.ipv4.tcp_fastopen = 3 # 启用 SYN Cookie(防止 SYN Flood 攻击同时不影响正常连接) net.ipv4.tcp_syncookies = 1 # 应用变更 sudo sysctl -p
验证方式:
# 确认参数生效
sysctl net.ipv4.tcp_tw_reuse
sysctl net.ipv4.ip_local_port_range
# 用 curl 测量 TCP Fast Open 效果(需要服务器和客户端都支持)
curl -o /dev/null -s -w"TCP Fast Open: %{time_connect}s
"
https://api.example.com/product/detail?id=12345
优化9:网卡多队列和 RSS,提升网络吞吐
如果服务器网卡支持多队列(RSS),可以让网卡中断分散到多个 CPU 核心处理,减少单核瓶颈。
# 查看网卡队列数 cat /proc/interrupts | grep eth0 | wc -l # 查看每个队列的中断分布 cat /proc/interrupts | grep eth0 # 开启网卡多队列(需要 ethtool) ethtool -l eth0 # 输出示例: # Channel parameters for eth0: # Pre-set maximums: # RX: 8 # TX: 8 # Current hardware settings: # RX: 1 ← 只有 1 个队列,没有充分利用 # TX: 1 # 开启 8 队列 ethtool -L eth0 combined 8
第五阶段:全链路压测与验证
灰度方案
不要一次性全量上线优化,先灰度到 10% 的流量,观察错误率和延迟变化。
# 灰度配置示例:按 IP 段灰度
geo $backend_pool {
default backend;
10.0.0.0/8 backend_optimized; # 内部网络走优化后的后端
}
upstream backend {
server 127.0.0.1:8080;
}
upstream backend_optimized {
server 127.0.0.1:8081; # 优化后的后端实例
}
监控指标
优化上线后,持续监控以下指标:
# 1. Nginx 响应时间分布(P50/P90/P99)
awk'{print $NF}'/var/log/nginx/access.log |
awk'{sum+=$1; arr[NR]=$1} END {
asort(arr);
print "P50:" arr[int(NR*0.5)];
print "P90:" arr[int(NR*0.9)];
print "P99:" arr[int(NR*0.99)];
}'
# 2. Nginx 缓存命中率
awk'/api// {print $NF}'/var/log/nginx/access.log |
grep -E"HIT|MISS"| sort | uniq -c
# 3. upstream_connect_time 分布
awk -F'"''{print $(NF-3)}'/var/log/nginx/access.log |
awk -F'= ''{print $2}'| awk -F.'{print $1}'|
sort | uniq -c | sort -rn | head -10
# 4. 错误率(5xx)
awk'{if ($9 >= 500) print}'/var/log/nginx/access.log | wc -l
最终效果对比
| 指标 | 优化前 | 优化后 | 降低幅度 |
|---|---|---|---|
| TTFB(首字节时间) | 487ms | 32ms | -93% |
| Total Response Time | 512ms | 48ms | -91% |
| upstream_connect_time | 25ms | 2ms | -92% |
| 缓存命中率(热门商品) | 0% | 95%+ | - |
| gzip 压缩率 | - | 72% | - |
| P99 响应时间 | 2000ms | 85ms | -96% |
补充一:Upstream 健康检查与故障转移
问题背景
如果后端有多个实例(多台服务器或多个容器),某个实例故障时 Nginx 需要能自动跳过故障节点,把请求转发到健康的实例上。如果健康检查没配置,Nginx 会继续往故障实例发请求,导致部分用户请求失败。
Nginx Plus 的主动健康检查
开源版 Nginx 的免费版本没有内置主动健康检查(只能靠被动失败检测),Nginx Plus 才支持主动健康检查。以下是开源版的替代方案。
开源版方案:max_fails + fail_timeout
upstream backend {
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8082 backup; # backup 机器,正常不参与负载均衡
}
max_fails=3:在 30 秒内连续失败 3 次,认为该 server 不可用
fail_timeout=30s:失败检测的时间窗口
backup:该 server 只在所有非 backup server 都不可用时才参与服务
用 OpenResty 实现主动健康检查
OpenResty 在 Nginx 基础上集成了 Lua,可以实现主动健康检查:
upstream backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}
# OpenResty 健康检查配置(需要 lua-resty-upstream-healthcheck)
location /health_check {
default_type text/plain;
content_by_lua '
local hc = require("resty.upstream.healthcheck")
local ok, err = hc.ping_node({
host = "127.0.0.1",
port = 8080,
timeout = 1000,
})
if ok then
ngx.say("OK")
else
ngx.say("FAIL: " .. err)
end
';
}
验证健康检查
# 模拟后端故障(杀掉一个后端实例) systemctl stop myapp@8080 # 观察 Nginx 是否自动跳过故障实例 watch -n 1'curl -s http://localhost/upstream_status' # upstream_status 显示: # Server: 127.0.0.1:8080 DOWN ← 被标记为 down # Server: 127.0.0.1:8081 UP
补充二:SSL/TLS 优化
问题背景
HTTPS 连接建立时,SSL/TLS 握手本身会引入延迟。如果证书配置不当,每次新建连接都需要完整的握手过程,会让 TTFB 多出几十甚至上百毫秒。启用 TLS 会话复用,可以让重连时跳过完整的握手过程。
SSL 证书配置
server {
listen 443 ssl http2;
server_name api.example.com;
# 证书文件
ssl_certificate /etc/nginx/ssl/api.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/api.example.com.key;
# TLS 版本控制(禁用 TLS 1.0 和 1.1,它们有安全漏洞)
ssl_protocols TLSv1.2 TLSv1.3;
# 加密套件(禁用弱加密算法,只启用 AEAD 系列)
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on;
# 开启 OCSP Stapling(浏览器不用向 CA 查询证书状态,减少一次 DNS 查询和 HTTP 请求)
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# SSL 会话缓存(减少 TLS 握手开销)
ssl_session_cache shared50m; # 50MB 缓存,可存储约 10 万个会话
ssl_session_timeout 1d; # 会话缓存有效期
ssl_session_tickets on; # 启用会话票据(支持分布式 TLS 会话恢复)
}
验证 SSL 配置质量
# 用 openssl 测试 SSL 连接和证书 echo| openssl s_client -connect api.example.com:443 -servername api.example.com # 查看证书信息 echo| openssl s_client -connect api.example.com:443 2>/dev/null | openssl x509 -noout -dates -issuer -subject # 测试 TLS 1.3 支持 openssl s_client -connect api.example.com:443 -tls1_3 < /dev/null 2>&1 | grep"TLSv1.3" # 用 testssl.sh 做完整的 SSL 安全检测 docker run --rm -ti drwetter/testssl.sh api.example.com # 检查项:TLS 版本、加密套件、证书链、OCSP Stapling、CRL/HSTS 等
补充三:502/504 错误排查
问题背景
502 Bad Gateway 和 504 Gateway Timeout 是 Nginx 作为反向代理时最常见的错误。502 表示 Nginx 收到了后端返回的错误响应(如 500 错误),504 表示 Nginx 等待后端超时。
502 错误排查
# 第一步:查看 Nginx error log tail -f /var/log/nginx/error.log | grep -i"502|upstream" # 第二步:查看 upstream 响应状态 # 如果是 PHP-FPM,通常是 fastcgi_pass 配置问题 # 如果是 Java,通常是 uwsgi_pass 或 proxy_pass 问题 # 第三步:检查后端服务状态 systemctl status php-fpm systemctl status gunicorn journalctl -u nginx --since"5 minutes ago"| grep -i error # 第四步:检查后端日志 # PHP-FPM: /var/log/php-fpm/www-error.log # Node.js: journalctl -u nodejs-app --since "5 minutes ago"
常见原因:
PHP-FPM/后端服务崩溃(OOM Kill、配置错误)
PHP-FPM 的max_children不足,请求排队超时
后端进程数达到上限,无法接受新连接
后端配置有语法错误,重启后没有正常加载
504 错误排查
# 504 超时通常是以下原因: # 1. proxy_read_timeout 设置过小 # 默认 60s,如果后端处理超过 60s 就超时 # 查看:grep "proxy_read_timeout" /etc/nginx/nginx.conf # 2. 后端服务本身处理慢(慢查询、死循环) # 查看后端慢查询日志 # 3. 网络延迟(Nginx 和后端之间的网络慢) # traceroute 或 mtr 到后端 IP mtr -r -c 10 127.0.0.1 # 4. upstream_connect_timeout 过小 # 如果 Nginx 和后端之间网络不稳定,连接建立超时
补充四:Nginx 常见配置错误案例
错误一:proxy_set_header Host 设置错误
# 错误配置:$host 变量在某些场景下会被设置为端口号
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $host; # 如果请求是 api.example.com:8080,$host 会变成 api.example.com:8080
}
# 正确配置:去掉端口号
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $http_host; # $http_host 永远是 host:port 格式
# 或者
proxy_set_header Host $host:$server_port; # 明确包含端口
}
错误二:proxy_buffering 导致的延迟
# 默认 proxy_buffering 是开启的
# 后端响应会先写入 buffer,buffer 满了才返回给客户端
# 对于需要实时推送的场景(如 SSE、long polling),需要关闭 buffer
location /sse/ {
proxy_pass http://backend;
proxy_buffering off; # 关闭缓冲
proxy_cache off; # 关闭缓存
proxy_set_header Connection ""; # 清除 Connection 头
chunked_transfer_encoding on; # 启用 chunked 编码
}
错误三:location 匹配优先级错误
# location 匹配优先级:最长前缀匹配 > 正则匹配(按配置文件顺序)> ^~
# 场景:想拦截所有 /api/ 开头的请求
location /api/ { } # 匹配所有 /api/*(最长前缀)
location ~ ^/api/v1$ { } # 只匹配 /api/v1(正则,优先级低于前缀)
location ^~ /api/v1 { } # 匹配 /api/v1 开头的请求,禁用正则匹配
# 如果 /api/v1 写成正则且在前面,普通 /api/ 请求会被错误匹配到正则
# 正确做法:把精确匹配放前面,或使用 ^~ 修饰符
总结:优化是分层递进的过程
从 500ms 到 50ms,不是一个 magic bullet,而是多层优化叠加的结果:
网络层:keepalive 减少连接建立开销(-20~50ms)
缓存层:proxy_cache 挡住重复请求(命中时 -400~500ms)
传输层:gzip 压缩减少传输体积(-30~60ms)
协议层:HTTP/2 消除队头阻塞(-20~50ms)
系统层:内核参数调优减少网络延迟(-5~20ms)
后端层:Redis 缓存减少数据库查询(-200~400ms)
优化过程中最重要的不是配置调优,而是测量-改配置-再测量-对比效果的闭环。每一次改动都要有基线数据支撑,避免"我觉得这样会快"的主观判断。
最后提醒:优化结果需要回滚预案。每一个配置改动,最好记录原始值,一旦线上出现异常,第一时间回滚到上一个稳定状态,再排查原因。
-
接口
+关注
关注
33文章
9630浏览量
157762 -
服务器
+关注
关注
14文章
10426浏览量
91835 -
nginx
+关注
关注
0文章
200浏览量
13239
原文标题:性能优化实战:如何将Nginx响应时间从500ms降至50ms
文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
想用TC275的一个CAN节点往外发不同周期(10ms的、20ms的、50ms的)的报文,如何实现呢?
调试程序时While循环里的延时是50ms改为500ms后恢复正常
Zigbee 定时发送时间设置小于500MS时候,接收端会丢包怎么解决?
如何配置50ms的定时器中断
如何在同一个定时器里用50ms和20ms?
为什么ucosii延时函数计数250左右才能实现亮灭各500ms?
ch582如何将500MS广播时平均电流控制在10uA内?
怎么缩短freemodbus组件作为从机的响应时间呢?
华硕推出全新显示器 144Hz刷新率+1ms响应时间
LTC6994 LTC6993演示电路延迟单次(延迟50ms,单次10ms)
如何将Nginx响应时间从500ms降至50ms
评论