首页 › 博客 › 文章

Nginx 504 与 PHP-FPM 卡死:从日志到定位

Nginx 504 与 PHP-FPM 卡死:从日志到定位

504 是 Nginx 等上游超时,不是 PHP 一定崩了。先看 Nginx error_log 里 upstream timed out,再查 PHP-FPM slowlog 与 MySQL 慢查询;把 request_terminate_timeout、pm.max_children、外部请求超时配好并压测复现,才算做对。

504 / 502 / 499 的区别

做 Nginx 504 排查,第一步是分清状态码。三者都出现在 Nginx 日志,但成因完全不同,混为一谈会直接走错方向。

状态码 含义 典型日志 根因方向
504 Gateway Timeout,Nginx 等上游 PHP-FPM 超时未返回 upstream timed out (110: Connection timed out) while reading response header from upstream PHP 执行太久、FPM 忙、外部 API 卡住
502 Bad Gateway,Nginx 连不上或收到无效响应 connect() to unix:/run/php/php8.3-fpm.sock failed (111: Connection refused) FPM 没启动、socket 权限错、进程崩
499 Client Closed Request,客户端先断开 无 upstream 错误,access_log 状态码 499 前端超时、用户关页、CDN 回源超时

快速定位命令:

# 2026 年常见路径(Debian/Ubuntu 上 PHP 8.3/8.4)
# 看 504 的 upstream 报错
grep 'upstream timed out' /var/log/nginx/error.log | tail -20

# 看 access_log 里 504/499 分布
awk '$9 ~ /^(504|499)$/ {print $9}' /var/log/nginx/access.log | sort | uniq -c

# 确认 FPM 在不在
systemctl status php8.3-fpm
ls -l /run/php/php8.3-fpm.sock

踩坑:很多人一看 504 就去调大 fastcgi_read_timeout,结果只是把报错推迟。判断做对的标准是——改完后用压测复现,504 消失且没有变成 502 或 499。

慢查询与外部请求超时

504 最常见的根因不是 Nginx,而是 WordPress 在等 MySQL 或外部 HTTP。2026 年 WordPress 6.7+ 仍大量依赖 WP_Query、WP_Http,外部请求默认没有短超时。

MySQL 慢查询

先看有没有慢查询。MySQL 8.0/8.4 上:

# 临时开慢日志(重启失效,适合线上止血排查)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';

# 实时抓
tail -f /var/log/mysql/slow.log

# 用 mysqldumpslow 汇总
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

WordPress 侧常见杀手:未分页的 WP_Query、缺失索引的 postmeta 查询、Autoload 巨大的 wp_options。检查 autoload:

SELECT option_name, LENGTH(option_value) AS size
FROM wp_options
WHERE autoload = 'yes'
ORDER BY size DESC LIMIT 10;

如果单行超过 800KB,考虑清理或改为 autoload = ‘no’。这和对象缓存策略直接相关,可参考 WordPress 对象缓存:Redis 到底能解决什么问题 来把重复查询挡在数据库前。

外部 HTTP 请求卡死

WordPress 的 wp_remote_get() 默认 timeout 是 5 秒,但很多插件传了更长的值,或在循环中调用。2026 年仍建议显式设短超时:

获取报价

邮箱 + 需求 + 预算区间,24 小时内回复报价。

$response = wp_remote_get( 'https://api.example.com/data', array(
    'timeout'     => 3,
    'redirection' => 2,
    'blocking'    => true,
) );

if ( is_wp_error( $response ) ) {
    error_log( 'external api fail: ' . $response->get_error_message() );
    return;
}

$body = wp_remote_retrieve_body( $response );

同时看 PHP 侧是否卡在 curl:

# 看当前 FPM 进程在干什么(需装 strace)
strace -p $(pgrep -f 'php-fpm: pool www' | head -1) -s 200 -tt

# 或者用 gdb 打栈(php8.3 带 debug symbol 时)
gdb -p $(pgrep -f 'php-fpm: pool www' | head -1) -ex 'bt' -batch

验证做对:外部接口挂掉时,页面应在 3 秒后降级或报错,而不是 60 秒后 Nginx 抛 504。回退方法:把 timeout 改回原值前先备份 functions.php 或插件文件。

FPM 进程池与队列

即使 SQL 和外部请求都快,FPM 池子满了也会排到超时。2026 年 PHP-FPM 配置仍在 /etc/php/8.3/fpm/pool.d/www.conf(或 8.4)。

关键参数

  • pm.max_children:同时最多几个 worker。太小会排队。
  • pm.max_requests:处理多少请求后重启,防内存泄漏。
  • request_terminate_timeout:单个请求硬杀时间,必须小于 Nginx 的 fastcgi_read_timeout。
  • listen.backlog:socket 队列长度,满了就丢连接。
# /etc/php/8.3/fpm/pool.d/www.conf 片段
pm = dynamic
pm.max_children = 40
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 12
pm.max_requests = 500

request_terminate_timeout = 30s
request_slowlog_timeout = 5s
slowlog = /var/log/php8.3-fpm.slow.log

listen = /run/php/php8.3-fpm.sock
listen.backlog = 511

Nginx 侧对应(/etc/nginx/conf.d/site.conf 或 sites-enabled):

location ~ .php$ {
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_read_timeout 60s;
    fastcgi_send_timeout 60s;
    fastcgi_connect_timeout 10s;
}

注意:request_terminate_timeout(30s)必须 < fastcgi_read_timeout(60s),否则 PHP 先被杀、Nginx 还在等,日志会变成 502 而非干净的 504。

怎么看队列堵了

# FPM 状态页需先开 pm.status_path = /fpm-status
curl http://127.0.0.1/fpm-status

# 关注:
# listen queue: 队列长度
# active processes: 活跃进程
# max children reached: 是否触顶

# 系统层看 socket 队列
ss -lx | grep php8.3-fpm

如果 listen queue 持续大于 0 且 max children reached 大于 0,就是池子不够或请求太慢。先加 max_children(受内存限制:每个 FPM 进程约 30-60MB),再回头查慢请求。

实施步骤:从日志到止血与根治的完整流程

  1. 用 grep 'upstream timed out' /var/log/nginx/error.log 确认是 504 而非 502/499,记录时间点。
  2. 同时段查 PHP-FPM slowlog(/var/log/php8.3-fpm.slow.log),拿到卡住的文件与函数名。
  3. 开 MySQL slow log 抓 2 秒以上查询,检查 wp_options autoload 与缺失索引。
  4. 检查所有 wp_remote_get/wp_remote_post 的 timeout 参数,统一压到 3-5 秒。
  5. 调 FPM:request_terminate_timeout 小于 fastcgi_read_timeout,按需加 pm.max_children。
  6. 用 ab 或 wrk 压测复现,确认 504 消失且无 502/499 转移。

压测示例:

# 模拟 20 并发、200 请求
ab -n 200 -c 20 https://example.com/

# 或 wrk
wrk -t4 -c50 -d30s https://example.com/

失败回退:改 www.conf 前先 cp www.conf www.conf.bak;改完 php-fpm8.3 -t 校验语法,再 systemctl reload php8.3-fpm。若 reload 后 502,立刻还原备份并 reload。

临时止血与根治

临时止血与根治

临时止血

线上已经 504 时,先恢复服务:

# 1. 重启 FPM 清掉僵死 worker
systemctl restart php8.3-fpm

# 2. 临时放宽 Nginx 超时(争取排查时间,不是解药)
sed -i 's/fastcgi_read_timeout .*/fastcgi_read_timeout 120s;/' /etc/nginx/conf.d/site.conf
nginx -t && systemctl reload nginx

# 3. 停掉明显作恶的插件(进库或文件改名)
UPDATE wp_options SET option_value='' WHERE option_name='active_plugins';

第 3 步会关掉所有插件,仅用于争取时间;之后要逐个恢复。更安全的做法是移走单个插件目录:mv wp-content/plugins/heavy-plugin wp-content/plugins/.heavy-plugin.off。

根治

止血后必须做根因修复:

  • 给慢查询加索引:ALTER TABLE wp_postmeta ADD INDEX meta_key (meta_key(191));
  • 外部请求全部走短超时 + 失败降级,必要时用 Transients API 缓存结果。
  • FPM 常驻参数固化进配置管理(Ansible/Puppet),不靠手工改。
  • 引入 APM 或日志告警:504 出现 5 次/分钟就通知。

如果站点本身流量模型复杂、需要长期有人值守,可以交给专业团队维护。我们提供的 WordPress 企业网站开发 服务包含部署架构评审与故障兜底,适合把这类问题前置到上线前解决。

另一个常被忽略的根治动作是备份与回滚能力:修改 FPM/Nginx 前确保能还原。可参考 WordPress 备份策略:3-2-1 原则与恢复演练 建立演练习惯。

常见问题

调大 fastcgi_read_timeout 就能解决 504 吗?

不能。那只是让 Nginx 等更久,请求仍卡在 PHP 或 MySQL。必须配合 slowlog 找到慢点,否则超时只是被推迟。

request_terminate_timeout 设成 0 是不是更好?

不是。0 表示不限制,坏请求会永久占住 worker,池子迅速耗尽。应设为小于 fastcgi_read_timeout 的值,如 30s。

为什么重启 FPM 后暂时好了又复发?

说明有持续慢请求或泄漏。重启只清了当前进程,根因(慢 SQL、外部 API、插件循环)还在,需按流程继续查。

可执行的动作清单

  1. 今天就在 error.log 里跑一遍 grep 'upstream timed out',确认 504 规模。
  2. 打开 PHP-FPM slowlog 并设 request_slowlog_timeout = 5s。
  3. 检查 wp_options autoload 体积,超过 800KB 的行做清理。
  4. 把所有 wp_remote_* 的 timeout 显式设为 3-5 秒。
  5. 校准 request_terminate_timeout 与 fastcgi_read_timeout 的层级关系并 reload。

想让 WordPress 在高并发下不再卡死?看我们的 WordPress 技术服务,或从 技术栏目 继续深入。

参考资料与延伸阅读

WPDaiwei

WPDaiwei-专业Wordpress网站定制开发提供商

需要 WordPress 开发服务?

24 小时内免费出方案与报价,国内国外都接单。

QQ