0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何将Nginx响应时间从500ms降至50ms

马哥Linux运维 来源:马哥Linux运维 2026-05-18 16:29 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

引言:从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的)的报文,如何实现呢?

    是20ms50ms,那我如何实现不同周期的发送呢?我想了个方法是用系统定时器STM,分别定时10ms、20ms50ms等然后进中断,在各
    发表于 02-06 06:42

    调试程序时While循环里的延时是50ms改为500ms后恢复正常

    下一个值。但用高亮模式运行时,接受和显示一切正常。最后发现调试程序时While循环里的延时是50ms,改为500ms后一切恢复正常。
    发表于 08-26 22:13

    Zigbee 定时发送时间设置小于500MS时候,接收端会丢包怎么解决?

    Zigbee 定时发送时间设置小于500MS时候,接收端会丢包怎么解决?用2块Zigbee板子,一个当协调器,一个当路由,PC机上一个串口助手往协调器发送数据,协调器发送无线到路由,路由接收到数据后
    发表于 03-15 15:08

    如何配置50ms的定时器中断

    我想用50ms的定时器中断……如何配置这个…我没有逻辑,我已经给1 MHz的时钟定时器模,,时期写的五万计数..所以是50ms计数周期。但我每次都被打断…我在TC模式下配置了中断…所以我想我应该中断
    发表于 01-17 06:46

    如何在同一个定时器里用50ms和20ms

    请教个问题:定时器模块,Timerx_Init(10000,7199);定时器初始化为1S,怎么在同一个定时器里用50ms和20ms?
    发表于 10-12 10:22

    为什么ucosii延时函数计数250左右才能实现亮灭各500ms

    ); }}u8led(void){控制数码管1;计数并判断小于25时亮,25-50灭并重新计数。实现数码管亮500ms,灭500ms。delay_ms(1);控制数码管2;delay_
    发表于 10-29 21:55

    如何实现流水灯以间隔500ms时间闪烁?

    如何实现流水灯以间隔500ms时间闪烁?
    发表于 11-30 06:04

    ch582如何将500MS广播时平均电流控制在10uA内?

    我们现在在做一个电池供电的蓝牙机设备,要求空闲时休眠,设备仍以500ms的间隔广播,系统平均功耗要在10uA内。我用preipheral例程修改广播间隔到800,使能HAL_SLEEP,开发板测量
    发表于 08-10 07:27

    怎么缩短freemodbus组件作为机的响应时间呢?

    freemodbus组件设置为机,波特率115200,接收用串口中断方式,响应时间500ms,主机发送命令间隔需在500ms以上通信才正常。请问想缩短这个
    发表于 04-14 10:28

    单音脉冲在50ms时间间隔电路图

    单音脉冲在50ms时间间隔
    的头像 发表于 06-10 10:08 3488次阅读
    单音脉冲在<b class='flag-5'>50ms</b>的<b class='flag-5'>时间</b>间隔电路图

    一种实现50ms断电维持供电的电路设计_杜培德

    一种实现50ms断电维持供电的电路设计_杜培德
    发表于 01-08 10:30 9次下载

    用 RL78/G13 实现定时500ms中断反转呼吸灯

    RL78/G13开发板定时器Channel 0应用----定时500ms中断反转呼吸灯视频
    的头像 发表于 06-14 05:02 6252次阅读

    华硕推出全新显示器 144Hz刷新率+1ms响应时间

    近日,华硕推出了全新显示器TUF GAMING VG249Q,主打144Hz刷新率与1ms响应时间
    的头像 发表于 10-28 17:13 3714次阅读

    LTC6994/LTC6993演示电路延迟单次(延迟50ms,单次10ms)

    LTC6994/LTC6993演示电路延迟单次(延迟50ms,单次10ms)
    发表于 04-10 10:35 4次下载
    LTC6994/LTC6993演示电路延迟单次(延迟<b class='flag-5'>50ms</b>,单次10<b class='flag-5'>ms</b>)

    LTC6994 LTC6993演示电路延迟单次(延迟50ms,单次10ms)

    LTC6994 LTC6993演示电路延迟单次(延迟50ms,单次10ms)
    发表于 06-09 20:54 93次下载
    LTC6994 LTC6993演示电路延迟单次(延迟<b class='flag-5'>50ms</b>,单次10<b class='flag-5'>ms</b>)