找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3128

积分

0

好友

416

主题
发表于 昨天 23:54 | 查看: 15| 回复: 0

一、概述

1.1 背景介绍

在运维工作中,Nginx的502和504错误可以说是“老熟人”了。业务高峰时突然大面积502,或是某个接口偶发504导致用户投诉,这些场景想必很多运维工程师都经历过。

虽然这两个错误码同属网关错误,但背后的根因和排查思路截然不同。常见误区是遇到502就重启PHP-FPM,碰上504就盲目加大超时时间,这种“头痛医头”的做法往往无法根治问题。

本文将系统梳理Nginx 502/504错误的排查逻辑,从核心原理、详细排查步骤到优化方案,并结合我们团队在实际生产环境中积累的运维经验,提供一套完整的解决方案和一个实用的一键排查脚本。

1.2 502与504的区别

首先要厘清这两个状态码的本质区别:

502 Bad Gateway(错误网关)

  • Nginx作为反向代理,已将请求转发给后端服务。
  • 但后端返回了一个Nginx无法理解的响应,或者直接断开了连接。
  • 通俗理解:后端服务挂了,或者返回了异常数据。

504 Gateway Timeout(网关超时)

  • Nginx作为反向代理,已将请求转发给后端服务。
  • 但在等待了设定的超时时间后,后端仍未返回任何响应。
  • 通俗理解:后端处理太慢,Nginx等不及了。

一句话总结:502是后端“不行了”,504是后端“太慢了”。

1.3 适用场景

  • 场景一:PHP-FPM作为后端(经典LNMP架构)。
  • 场景二:Nginx反向代理到Java/Go/Python等自研应用服务。
  • 场景三:Nginx作为负载均衡器,代理到多个上游(upstream)服务。
  • 场景四:WebSocket等长连接场景。

1.4 环境要求

组件 版本要求 说明
Nginx 1.14+ 本文命令适用于主流版本
操作系统 CentOS 7+ / Ubuntu 18.04+
后端服务 PHP-FPM / Tomcat / 自研服务 示例以PHP-FPM为主

二、详细步骤

2.1 502错误排查

遇到502错误,建议按以下顺序排查,可覆盖绝大多数情况。

◆ 2.1.1 检查后端服务状态

502最常见的原因就是后端服务进程不在了。首先确认服务是否在运行:

# 检查PHP-FPM状态
systemctl status php-fpm
ps aux | grep php-fpm | grep -v grep

# 检查Java服务(如Tomcat)
systemctl status tomcat
ps aux | grep java | grep -v grep

# 检查端口是否在监听
ss -tlnp | grep 9000    # PHP-FPM默认端口
ss -tlnp | grep 8080    # Tomcat/Spring Boot常见端口

# 如果使用Unix Socket连接
ls -la /run/php-fpm/www.sock
# 确认socket文件存在且权限正确(Nginx进程用户可读)

经验之谈:曾遇到PHP-FPM父进程还在,但socket文件被误删的情况,Nginx无法连接同样报502。因此,不能只看进程是否存在,还需确认监听端点(端口或socket)正常。

◆ 2.1.2 检查Nginx错误日志

Nginx的error.log是定位502问题的金矿,所有连接后端的错误信息都会记录于此。

# 实时查看错误日志
tail -f /var/log/nginx/error.log

# 过滤与502/上游连接相关的错误
grep "502\|upstream\|connect\|failed" /var/log/nginx/error.log | tail -50

常见的错误信息及含义:

错误信息 含义 解决方向
connect() failed (111: Connection refused) 连接被拒绝,后端服务未启动或端口错误 检查后端服务状态与端口
connect() failed (113: No route to host) 网络不可达 检查防火墙、网络路由
upstream prematurely closed connection 上游(后端)提前关闭了连接 后端可能因OOM(内存溢出)崩溃
recv() failed (104: Connection reset by peer) 对端重置了连接 后端处理出现异常
no live upstreams 所有配置的上游服务器都不可用 检查所有后端节点

◆ 2.1.3 检查PHP-FPM状态(LNMP场景)

如果后端是PHP-FPM,以下检查至关重要:

# 查看PHP-FPM状态页(需先配置开启)
curl http://127.0.0.1/php-fpm-status

# 查看PHP-FPM总进程数
ps aux | grep "php-fpm" | grep -v grep | wc -l

# 查看PHP-FPM实际处理请求的子进程状态
ps aux | grep "php-fpm: pool" | grep -v grep

# 检查PHP-FPM错误日志
tail -100 /var/log/php-fpm/www-error.log

开启PHP-FPM状态页的配置:

; /etc/php-fpm.d/www.conf
pm.status_path = /php-fpm-status
# nginx配置中增加一个location来访问状态页
location /php-fpm-status {
    fastcgi_pass unix:/run/php-fpm/www.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
    allow 127.0.0.1;
    deny all;
}

PHP-FPM进程池耗尽是502的常见原因。如果状态页显示所有子进程都是“active”(忙碌)状态,且“idle processes”(空闲进程)为0,说明进程池已满,新请求无法被处理:

# 状态页输出示例:
pool:                 www
process manager:      dynamic
start time:           01/Jan/2024:10:00:00 +0800
start since:          86400
accepted conn:        123456
listen queue:         0
max listen queue:     100
listen queue len:     128
idle processes:       0          # 危险信号:空闲进程为0
active processes:     50         # 活跃进程数
total processes:      50
max active processes: 50
max children reached: 10         # 达到最大进程数的次数,增长快说明进程不足
slow requests:        5

◆ 2.1.4 检查连接数和资源限制

有时服务进程还在,但系统或进程本身的资源达到上限,也会导致502。

# 查看系统已使用的文件描述符(连接本质是文件)
cat /proc/sys/fs/file-nr
# 输出:已用数量   未用数量   最大值

# 查看Nginx worker进程的总连接数概况
ss -s

# 查看特定后端端口(如9000)的当前连接数
ss -ant | grep :9000 | wc -l

# 查看PHP-FPM主进程使用的文件描述符数量
ls /proc/$(pgrep -o php-fpm)/fd | wc -l

# 查看当前shell的资源限制(如最大打开文件数)
ulimit -n

# 查看系统级别的资源限制配置
cat /etc/security/limits.conf | grep -v "^#"

2.2 504错误排查

504错误意味着后端处理时间超过了Nginx设置的超时阈值。

◆ 2.2.1 确认超时配置

首先检查当前Nginx中关于超时的各项配置。

# 查看Nginx配置中的所有超时参数
grep -r "timeout" /etc/nginx/ | grep -v "#"

Nginx中影响504的关键超时参数如下:

# 与后端服务器建立连接的超时时间(默认60秒)
proxy_connect_timeout 60s;

# 定义从后端服务器读取响应数据的超时时间(默认60秒)
proxy_read_timeout 60s;

# 定义将请求发送到后端服务器的超时时间(默认60秒)
proxy_send_timeout 60s;

# FastCGI(PHP-FPM)场景下的对应参数
fastcgi_connect_timeout 60s;
fastcgi_read_timeout 60s;
fastcgi_send_timeout 60s;

◆ 2.2.2 分析后端实际响应时间

仅看配置不够,需要知道后端处理请求实际耗时。可以配置Nginx的日志格式,记录详细的上下游时间。

# 在 nginx.conf 的 http 块中定义一个详细日志格式
log_format detailed '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    'rt=$request_time uct="$upstream_connect_time" '
                    'uht="$upstream_header_time" urt="$upstream_response_time"';

# 在 server 或 location 块中使用该格式
access_log /var/log/nginx/access.log detailed;

各时间字段含义:

  • $request_time:Nginx处理请求的总耗时。
  • $upstream_connect_time:Nginx与后端建立连接所花费的时间。
  • $upstream_header_time:Nginx从后端收到响应头所花费的时间。
  • $upstream_response_time:Nginx从后端收到完整响应所花费的时间。这个时间超过 proxy_read_timeout 就会触发504。

分析慢请求:

# 找出响应时间超过5秒的请求(假设日志格式已包含urt)
awk '$NF > 5 {print $0}' /var/log/nginx/access.log | tail -20

# 按接口(URL)统计平均响应时间(简化示例,实际需根据日志格式调整)
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

◆ 2.2.3 排查后端处理慢的原因

504的根源在于后端。需要根据后端的类型进行针对性排查。

# 如果是PHP,开启并查看慢执行日志
# 编辑 /etc/php-fpm.d/www.conf
slowlog = /var/log/php-fpm/www-slow.log
request_slowlog_timeout = 3s  # 执行超过3秒的请求会被记录

# 查看PHP慢日志
tail -100 /var/log/php-fpm/www-slow.log

# 如果是MySQL慢查询导致
tail -100 /var/log/mysql/slow.log

# 如果是Java应用,可以使用jstack抓取线程转储,分析卡在何处
jstack <PID> > /tmp/thread_dump.txt

◆ 2.2.4 网络延迟检查

如果Nginx与后端服务部署在不同机器,网络也可能是瓶颈。

# 基础ping测试,检查延迟和丢包
ping -c 10 后端服务器IP

# TCP连接测试,测量建立TCP连接的时间
time nc -zv 后端服务器IP 端口

# 使用mtr进行更详细的网络路径诊断
mtr --report 后端服务器IP

2.3 常见原因汇总

◆ 502常见原因

原因 表现 解决方案
后端服务崩溃 进程不存在,端口无监听 重启服务,并排查崩溃原因(日志、OOM等)
PHP-FPM进程池耗尽 活跃进程数达到 pm.max_children 增加进程数上限,或优化代码减少请求处理时间
Socket文件权限问题 Nginx worker进程用户无权访问socket文件 检查并修正socket文件的所有者和权限
系统连接数耗尽 大量TIME_WAIT状态连接 调整net.ipv4.tcp_tw_reuse等内核参数
后端进程被OOM Killer终止 dmesg或系统日志中有oom-killer记录 增加内存,或优化应用内存使用

◆ 504常见原因

原因 表现 解决方案
SQL慢查询 PHP慢日志或MySQL慢日志中存在耗时长的SQL 优化SQL语句,添加合适的索引
调用外部API/服务慢 依赖的第三方接口响应缓慢 增加调用超时控制,考虑异步化或降级策略
代码逻辑问题(死循环/阻塞) CPU使用率高但请求不返回 审查应用代码逻辑
资源竞争(锁等待) 大量进程等待同一个锁(数据库行锁、文件锁等) 优化锁策略,减少临界区
Nginx超时配置过短 业务逻辑本身就需要较长时间处理(如文件上传、报表生成) 根据业务实际情况,合理增加超时时间

2.4 解决方案

◆ 2.4.1 502解决方案

PHP-FPM进程池优化配置示例:

; /etc/php-fpm.d/www.conf
[www]
...
; 进程管理方式,dynamic为动态管理
pm = dynamic
; 最大子进程数,根据服务器内存计算(内存总量 / 单进程平均内存占用)
pm.max_children = 100
; 启动时创建的子进程数
pm.start_servers = 20
; 保证最少空闲进程数,用于应对突发请求
pm.min_spare_servers = 10
; 保证最多空闲进程数,过多会被回收
pm.max_spare_servers = 30
; 每个子进程处理多少请求后自动重启,防止内存泄漏累积
pm.max_requests = 500

系统连接数优化(内核参数):

# 编辑 /etc/sysctl.conf
# 扩大本地端口范围
net.ipv4.ip_local_port_range = 1024 65535
# 允许复用处于TIME_WAIT状态的连接
net.ipv4.tcp_tw_reuse = 1
# 减少TIME_WAIT状态的超时时间
net.ipv4.tcp_fin_timeout = 30
# 提高系统同时接受连接的最大队列长度
net.core.somaxconn = 65535

# 使配置生效
sysctl -p

◆ 2.4.2 504解决方案

Nginx超时与缓冲优化配置示例:

location /api/ {
    proxy_pass http://backend_server;

    # 连接后端超时,不宜过长
    proxy_connect_timeout 10s;

    # 读取后端响应超时,根据业务调整(如报表生成可设长)
    proxy_read_timeout 120s;

    # 发送请求到后端超时
    proxy_send_timeout 60s;

    # 缓冲区优化,提升性能
    proxy_buffer_size 64k;
    proxy_buffers 8 64k;
    proxy_busy_buffers_size 128k;
}

负载均衡(upstream)优化配置示例:

upstream backend {
    # 使用最少连接数算法,比默认轮询更均衡
    least_conn;

    server 192.168.1.10:8080 weight=5 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 weight=5 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:8080 backup; # 备份节点,当主节点全挂时启用

    # 启用到后端的连接复用,减少频繁建连开销
    keepalive 32;
}

server {
    location / {
        proxy_pass http://backend;
        # 启用keepalive必须的HTTP版本和头部设置
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

三、示例代码和配置

3.1 Nginx优化配置示例

以下是一个综合性的生产环境Nginx主配置 (nginx.conf) 示例:

user nginx;
worker_processes auto; # 自动设置为CPU核数
worker_rlimit_nofile 65535; # 提高worker进程能打开的文件数上限

error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

events {
    worker_connections 65535;
    use epoll; # Linux高效事件模型
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # 定义包含上下游时间的日志格式
    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"';

    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    keepalive_timeout 65;
    keepalive_requests 1000; # 一个连接最多处理的请求数

    # Gzip压缩配置
    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 4;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;

    # 上游服务定义
    upstream php_backend {
        server unix:/run/php-fpm/www.sock;
        keepalive 16;
    }

    upstream api_backend {
        least_conn;
        server 192.168.1.10:8080 weight=5 max_fails=3 fail_timeout=30s;
        server 192.168.1.11:8080 weight=5 max_fails=3 fail_timeout=30s;
        keepalive 32;
    }

    # 包含其他server配置
    include /etc/nginx/conf.d/*.conf;
}

3.2 PHP-FPM优化配置示例

; /etc/php-fpm.d/www.conf
[www]
user = nginx
group = nginx

; 使用Unix Socket,比TCP性能更好
listen = /run/php-fpm/www.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660

; 进程管理
pm = dynamic
pm.max_children = 100
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 30
pm.max_requests = 500
pm.process_idle_timeout = 10s

; 状态监控页
pm.status_path = /php-fpm-status
ping.path = /php-fpm-ping
ping.response = pong

; 慢日志
slowlog = /var/log/php-fpm/www-slow.log
request_slowlog_timeout = 3s

; 请求终止超时(全局)
request_terminate_timeout = 120s

; 错误日志
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on

3.3 一键排查脚本

这是一个集成的Bash脚本,可以快速检查与Nginx 502/504相关的多项指标,非常适合在故障发生时快速收集信息。

#!/bin/bash
# nginx_diagnose.sh - Nginx 502/504 综合排查脚本
# 使用方法: bash nginx_diagnose.sh

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

echo "========================================"
echo "Nginx 502/504 一键排查脚本"
echo "检查时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"

echo ""
echo -e "${YELLOW}=== 1. Nginx服务状态 ===${NC}"
if systemctl is-active --quiet nginx; then
    echo -e "${GREEN}[OK] Nginx正在运行${NC}"
else
    echo -e "${RED}[ERROR] Nginx未运行${NC}"
fi
nginx -t 2>&1 | head -5

echo ""
echo -e "${YELLOW}=== 2. 后端服务状态检查 ===${NC}"

# 检查PHP-FPM
if command -v php-fpm &> /dev/null; then
    if systemctl is-active --quiet php-fpm; then
        echo -e "${GREEN}[OK] PHP-FPM正在运行${NC}"
        fpm_count=$(ps aux | grep "php-fpm: pool" | grep -v grep | wc -l)
        echo "    当前PHP-FPM子进程数: $fpm_count"
    else
        echo -e "${RED}[ERROR] PHP-FPM未运行${NC}"
    fi
fi

# 检查常见后端端口监听情况
for port in 9000 8080 3000 5000; do
    if ss -tlnp | grep -q ":$port "; then
        echo -e "${GREEN}[OK] 端口 $port 已被监听${NC}"
    fi
done

echo ""
echo -e "${YELLOW}=== 3. 最近的Nginx错误日志 (502/504) ===${NC}"
if [ -f /var/log/nginx/error.log ]; then
    error_count=$(grep -c "502\|504\|upstream" /var/log/nginx/error.log 2>/dev/null || echo 0)
    echo "最近错误总数: $error_count"
    echo "最近10条相关错误:"
    grep -E "502|504|upstream|connect" /var/log/nginx/error.log | tail -10
else
    echo "未找到Nginx错误日志文件"
fi

echo ""
echo -e "${YELLOW}=== 4. 系统连接数统计 ===${NC}"
echo "当前TCP连接摘要:"
ss -s
echo ""
echo "各状态连接数统计:"
ss -ant | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn

echo ""
echo -e "${YELLOW}=== 5. 系统资源概览 ===${NC}"
echo "CPU使用率 (快照):"
top -bn1 | head -5
echo ""
echo "内存使用情况:"
free -h
echo ""
echo "磁盘使用情况:"
df -h | grep -v tmpfs

echo ""
echo -e "${YELLOW}=== 6. 文件描述符使用情况 ===${NC}"
echo "系统级别文件描述符:"
cat /proc/sys/fs/file-nr
echo "Nginx各进程文件描述符数量:"
for pid in $(pgrep nginx); do
    echo "PID $pid: $(ls /proc/$pid/fd 2>/dev/null | wc -l) 个fd"
done

echo ""
echo -e "${YELLOW}=== 7. Nginx超时配置检查 ===${NC}"
grep -r "timeout" /etc/nginx/ 2>/dev/null | grep -v "#" | head -20

echo ""
echo -e "${YELLOW}=== 8. Nginx Upstream配置检查 ===${NC}"
grep -r "upstream" /etc/nginx/ 2>/dev/null | grep -v "#" | head -20

echo ""
echo -e "${YELLOW}=== 9. 最近的慢请求 (>3秒) ===${NC}"
if [ -f /var/log/nginx/access.log ]; then
    awk -F'rt=' '{if(NF>1){split($2,a," ");if(a[1]>3)print $0}}' /var/log/nginx/access.log | tail -10
fi

echo ""
echo "========================================"
echo "检查完成。请重点关注以上标红[ERROR]项及错误日志内容。"
echo "========================================"

使用方法:

chmod +x nginx_diagnose.sh
./nginx_diagnose.sh

3.4 实际应用案例

◆ 案例一:高并发场景下的502

场景:电商大促期间,流量激增,前端出现大面积502错误。

排查过程:

  1. 查看Nginx错误日志

    tail -f /var/log/nginx/error.log
    # 输出: connect() to unix:/run/php-fpm/www.sock failed (11: Resource temporarily unavailable)

    错误11通常表示资源临时不可用,指向连接问题。

  2. 检查PHP-FPM进程池

    ps aux | grep "php-fpm: pool" | wc -l
    # 输出: 50

    进程数已达到配置的最大值(pm.max_children=50)。

  3. 查看PHP-FPM状态页

    curl http://127.0.0.1/php-fpm-status
    # 关键输出:
    # pool:                 www
    # max children reached: 156
    # idle processes:       0
    # active processes:     50

    max children reached数值很高且不断增长,idle processes为0,确认进程池已耗尽,新请求无进程可用。

解决方案(临时应急):

  1. 根据服务器剩余内存,适当提高 pm.max_children(例如从50调整到100)。
  2. 同时按比例增加 pm.start_serverspm.max_spare_servers 等参数。
  3. 执行 systemctl reload php-fpm 平滑重载配置。

后续优化

  • 优化PHP代码,减少单个请求的处理时间和内存占用。
  • 引入Redis缓存,减少对数据库的直接查询。
  • 建立运维监控和自动伸缩机制。

◆ 案例二:慢查询导致的504

场景:某个报表导出接口频繁报504超时,其他接口正常。

排查过程:

  1. 分析Nginx访问日志

    grep "api/report/export" /var/log/nginx/access.log | awk -F'rt=' '{print $2}' | sort -rn | head -5
    # 输出: 120.005, 120.001, 119.998

    响应时间集中在120秒,正好是Nginx proxy_read_timeout 的默认值,说明是触发了超时。

  2. 检查PHP-FPM慢日志

    tail -20 /var/log/php-fpm/www-slow.log
    # 脚本在执行一个复杂的SQL查询时卡住。
  3. 检查MySQL慢查询日志

    tail -30 /var/log/mysql/slow.log
    # 发现一条对百万级大表进行全表扫描且无索引的SELECT语句,执行耗时超过100秒。

解决方案:

  1. 紧急限流:在Nginx层对该报表接口进行限流,避免拖垮整个数据库。
  2. 优化SQL

    • WHERE 条件和 ORDER BY 字段添加复合索引。
    • 重写SQL,避免 SELECT *,只取必要字段。
    • 考虑分页查询或异步生成报表。
      
      -- 优化前
      SELECT * FROM order_detail WHERE create_time > '2024-01-01' ORDER BY id DESC;

    -- 优化后 (添加索引: ALTER TABLE order_detail ADD INDEX idx_time_status (create_time, order_status);)
    SELECT id, order_id, amount, create_time FROM order_detail
    WHERE create_time > '2024-01-01' AND order_status = 'completed'
    ORDER BY create_time DESC LIMIT 1000;

  3. 应用优化:对于超大数据量的导出,改为异步任务处理,完成后通知用户下载。

四、最佳实践和注意事项

4.1 最佳实践

◆ 4.1.1 Nginx性能调优参数

# 主配置文件优化项
user nginx;
worker_processes auto; # 与CPU核心数一致
worker_rlimit_nofile 65535; # 提高单个worker能打开的文件数上限

events {
    worker_connections 65535; # 每个worker的最大连接数
    use epoll; # Linux高效网络模型
    multi_accept on; # 一次接受多个新连接
}

http {
    # 文件描述符缓存,减少磁盘I/O
    open_file_cache max=65535 inactive=60s;
    open_file_cache_valid 80s;
    open_file_cache_min_uses 1;
    open_file_cache_errors off;

    # 反向代理缓冲区优化
    proxy_buffer_size 128k;
    proxy_buffers 4 256k;
    proxy_busy_buffers_size 256k;
    proxy_temp_file_write_size 256k;
}

◆ 4.1.2 后端服务优化建议

  • 连接池:确保数据库(如PDO持久连接)、Redis等中间件使用连接池。
  • 超时控制:在应用代码内部,为所有外部调用(HTTP API、数据库查询、缓存访问)设置合理的超时时间。
    // PHP示例:控制外部HTTP调用超时
    $context = stream_context_create([
        'http' => [
            'timeout' => 5 // 5秒超时
        ]
    ]);
    $response = file_get_contents('http://api.example.com', false, $context);
  • 异步处理:对于耗时操作(如发送邮件、处理图片、生成报表),使用消息队列(如RabbitMQ、Redis List)进行异步化。

◆ 4.1.3 监控与告警配置

建议使用Prometheus + Grafana等监控体系。以下为示例告警规则:

# Prometheus告警规则 (alert.rules.yml)
groups:
  - name: nginx_errors
    rules:
      - alert: NginxHigh5xxErrorRate
        expr: rate(nginx_http_requests_total{status=~"5.."}[5m]) / rate(nginx_http_requests_total[5m]) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Nginx 5xx错误率过高"
          description: "实例 {{ $labels.instance }} 5xx错误率超过5%,当前值 {{ $value | humanizePercentage }}"

      - alert: NginxUpstreamResponseTimeout
        expr: nginx_upstream_response_time_seconds{upstream!=""} > 10
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "Nginx上游响应时间过长"
          description: "上游 {{ $labels.upstream }} 响应时间超过10秒"

4.2 注意事项

◆ 4.2.1 配置注意事项

  1. 超时时间非越长越好:将 proxy_read_timeout 设为300秒可能掩盖真正的问题,应优先优化慢请求。
  2. 进程/线程数平衡:PHP-FPM、Java应用线程池等并非配置越多越好,需考虑内存和CPU上下文切换开销。
  3. 平滑重载:修改Nginx配置后使用 nginx -s reload,而非 restart,以保障连接不中断。
  4. 配置语法检查:执行 nginx -t 确认配置无误后再重载。

◆ 4.2.2 常见配置错误

错误现象 可能原因 解决方案
修改配置后reload无效 配置文件存在语法错误,reload失败但进程未退出 使用 nginx -t 严格测试,查看error.log
调整超时后504依旧 配置未在正确层级生效(http/server/location优先级不同) 确认超时参数配置在匹配的 location 块中
Upstream服务器无法连接 防火墙、安全组或SELinux策略阻止 检查 firewalld/iptablesgetseboolsestatus
Socket文件权限错误 Nginx worker用户(如nginx)与PHP-FPM socket文件用户/组不一致 确保 listen.ownerlisten.group 与Nginx用户匹配

◆ 4.2.3 兼容性与版本问题

  • HTTP版本:启用 keepalive 连接复用需要后端和Nginx都支持HTTP/1.1协议,并设置 proxy_http_version 1.1
  • Unix Socket vs TCP:Unix Socket性能更低耗,但仅适用于Nginx与后端在同一台主机的情况。
  • PHP版本差异:PHP 5.3、7.x、8.x 的PHP-FPM配置参数可能略有不同,迁移时需注意。

五、故障排查和监控

5.1 日志分析技巧

# 统计每分钟的502/504错误数量
awk '$9==502 || $9==504 {print $4}' /var/log/nginx/access.log | cut -d: -f1,2 | sort | uniq -c

# 找出返回502最多的URL(前10名)
awk '$9==502 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10

# 分析请求响应时间分布(快速评估性能)
awk -F'rt=' '{
  if(NF>1){
    split($2,a," ");
    t=a[1];
    if(t<0.1) fast++;
    else if(t<1) normal++;
    else if(t<3) slow++;
    else too_slow++;
  }
}
END {
  printf "Fast(<0.1s): %d\nNormal(<1s): %d\nSlow(1-3s): %d\nToo Slow(>3s): %d\n", fast, normal, slow, too_slow
}' /var/log/nginx/access.log

5.2 启用Nginx状态监控

# 在Nginx server块中配置
location /nginx_status {
    stub_status on; # 启用状态模块
    access_log off;
    allow 127.0.0.1; # 仅允许本机访问,或监控服务器IP
    deny all;
}

访问 http://your-server/nginx_status 可看到:

Active connections: 291
server accepts handled requests
 16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106
  • Active connections:当前活跃连接总数。
  • Reading:正在读取请求头的连接数。
  • Writing:正在向客户端发送响应的连接数。
  • Waiting:已处理完请求,处于 keep-alive 空闲状态的连接数。这是反映并发能力的关键指标之一。

5.3 简易告警脚本示例

一个基于日志分析的简单阈值告警脚本:

#!/bin/bash
# nginx_error_alert.sh

LOG_FILE="/var/log/nginx/access.log"
ERROR_THRESHOLD=10 # 每分钟5xx错误数阈值
SLOW_THRESHOLD=3   # 秒,慢请求阈值
SLOW_COUNT_THRESHOLD=5 # 每分钟慢请求数阈值

CURRENT_MINUTE=$(date -d '1 minute ago' '+%d/%b/%Y:%H:%M')

# 1. 检查5xx错误
ERROR_COUNT=$(awk -v min="$CURRENT_MINUTE" '$4 ~ min && $9 ~ /^5/ {count++} END {print count+0}' "$LOG_FILE")
if [ $ERROR_COUNT -gt $ERROR_THRESHOLD ]; then
    echo "[警报] 过去1分钟检测到 $ERROR_COUNT 个5xx错误,超过阈值 $ERROR_THRESHOLD。" | tee -a /tmp/nginx_alert.log
    # 此处可集成邮件、钉钉、企业微信等告警发送逻辑
fi

# 2. 检查慢请求数量 (假设日志格式包含rt=)
SLOW_COUNT=$(awk -v min="$CURRENT_MINUTE" -v thr="$SLOW_THRESHOLD" '$4 ~ min && match($0, /rt=([0-9.]+)/, m) && m[1] > thr {count++} END {print count+0}' "$LOG_FILE")
if [ $SLOW_COUNT -gt $SLOW_COUNT_THRESHOLD ]; then
    echo "[警告] 过去1分钟检测到 $SLOW_COUNT 个超过 ${SLOW_THRESHOLD}s 的慢请求。" | tee -a /tmp/nginx_alert.log
fi

可将此脚本加入crontab,每分钟执行一次。


六、总结

6.1 核心要点回顾

  • 502 Bad Gateway:根源在后端服务不可用(崩溃、进程池满、资源耗尽)。排查关键是看Nginx的error.log和检查后端进程状态。
  • 504 Gateway Timeout:根源在后端处理超时(SQL慢、外部API慢、代码阻塞)。排查关键是分析Nginx访问日志中的上游响应时间,并结合后端慢日志。
  • 科学排查:遵循从现象(错误码)到日志(Nginx error/access log),再到后端状态(进程、资源、慢日志)的排查路径,避免盲目操作。
  • 治本而非治标:不要习惯性地“重启服务”或“调大超时”,而应定位并解决引发问题的根本原因。

6.2 进阶学习方向

  1. 全链路追踪:接入SkyWalking、Jaeger、Zipkin等APM工具,可视化请求在Nginx、应用、数据库、缓存之间的完整路径和耗时,精准定位瓶颈。
  2. Service Mesh:了解Istio、Linkerd等服务网格技术,它们提供了更细粒度、更强大的流量管理、观测和安全性能力。
  3. 压力测试:定期使用wrk、ab、jmeter等工具对系统进行压测,提前发现性能瓶颈和容量上限。
  4. 弹性伸缩:在Kubernetes等容器化环境中,配置HPA(Horizontal Pod Autoscaler),实现基于CPU、内存或自定义指标(如QPS)的自动扩缩容。

6.3 参考资料

  • Nginx官方文档:Module ngx_http_proxy_module (代理模块)
  • PHP-FPM官方配置说明
  • 操作系统性能调优指南(针对Linux内核参数)

希望这份涵盖原理、步骤、脚本和案例的指南,能帮助你在遇到棘手的Nginx网关错误时,更快地定位和解决问题。技术问题的排查能力,正是在一次次这样的实战中积累起来的。欢迎在云栈社区分享你的排查经验和遇到的疑难杂症。


附录

A. 命令速查表

# ---------- Nginx 相关 ----------
nginx -t                    # 测试配置文件语法
nginx -s reload            # 平滑重载配置(不中断服务)
nginx -s stop              # 快速停止
tail -f /var/log/nginx/error.log  # 实时查看错误日志

# ---------- PHP-FPM 相关 ----------
systemctl status php-fpm   # 查看服务状态
systemctl reload php-fpm   # 平滑重载PHP-FPM配置
ps aux | grep php-fpm      # 查看进程信息
tail -f /var/log/php-fpm/www-slow.log # 查看慢执行日志

# ---------- 网络连接排查 ----------
ss -antup                  # 查看所有TCP/UDP连接及对应进程
ss -s                      # 查看连接统计摘要
netstat -tlnp              # 传统方式查看监听端口(如未安装ss)
lsof -i :80                # 查看占用80端口的进程

# ---------- 日志分析常用 ----------
grep "502\|504" /var/log/nginx/error.log          # 过滤错误
awk '$9==502{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn # 统计502的URL
# 分析最近1小时请求状态码分布
awk -v d="$(date -d '1 hour ago' '+[%d/%b/%Y:%H')" '$4 ~ d {code[$9]++} END{for(c in code) print c, code[c]}' access.log

B. HTTP状态码速查(网关相关)

状态码 含义 Nginx视角常见原因
400 Bad Request 客户端请求语法错误,Nginx无法理解。
403 Forbidden 访问被拒绝(权限不足、IP限制等)。
404 Not Found 请求的资源在Nginx上未找到。
499 Client Closed Request 客户端在Nginx处理期间主动关闭了连接。常出现在后端处理慢,用户不耐烦刷新时。
500 Internal Server Error 后端应用服务器内部错误(如PHP语法错误、Java异常)。
502 Bad Gateway Nginx从后端收到无效响应(后端进程崩溃、连接被拒等)。
503 Service Unavailable 服务暂时不可用(常用于主动限流、维护页面)。
504 Gateway Timeout Nginx在等待后端响应时超时

C. 排查决策流程图(简化版)


收到502/504告警
    │
    ├─ 如果是502
    │   ├─ 检查Nginx error.log
    │   ├─ 检查后端进程/端口是否存活
    │   ├─ 检查PHP-FPM/应用进程池是否耗尽
    │   ├─ 检查系统资源(内存、文件描述符)
    │   └─ 根据具体错误信息定位(连接拒绝、socket失效等)
    │
    └─ 如果是504
        ├─ 检查Nginx proxy_read_timeout等配置
        ├─ 分析access.log,确认上游响应时间(urt)
        ├─ 检查后端应用慢日志(PHP、Java)
        ├─ 检查数据库慢查询
        ├─ 检查外部API/依赖服务状态
        └─ 检查网络状况(跨机房调用时)



上一篇:Knowledge Bank:面向AI辅助开发的知识管理系统,解决团队规范传承难题
下一篇:eBPF技术详解:Linux内核可观测性与网络性能调优实战
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-18 21:08 , Processed in 0.981214 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表