[原创] WordPress 插件Gutenberg升级卡死排查与修复全记录

场景:某 WordPress 站点的插件升级故障
说明:/var/www/wordpress 为示例路径。
结论:PHP 缺少 zip 扩展 → WordPress 回退到纯 PHP 的 PclZip 解压 → 解 48MB 的 Gutenberg 包时撞穿 256MB 内存上限 → PHP Fatal → 页面中断,表现为「无任何报错地卡死」。装 php-pecl-zip 后 1.7 秒完成,问题彻底消失。

1、故障现象

在 WordPress 后台升级 Gutenberg 插件,页面输出停在:

正开始升级。这个过程在某些服务器上花的时间要长些,请耐心等待。
正在升级 Gutenberg 插件(1 / 1)

之后没有任何后续信息,不报错、不超时提示,插件也没有升级成功。可反复重现。

症状的迷惑性在于:WordPress 的批量升级是「一个请求跑完整个解压过程,成功了才输出结果」。中途 PHP Fatal 会导致 HTTP 响应被截断,浏览器只拿到半截页面,于是看起来像"卡住",实际是进程已经死了。
文章来源:https://www.codelast.com/

2、环境要点

WordPress 跑在 nginx + PHP-FPM(unix socket)的标准部署下,站点根目录为 /var/www/wordpress。
文章来源:https://www.codelast.com/

3、排查路径

3.1 先排除"资源不够"这类常见原因

怀疑项 实测结果 结论
磁盘满 磁盘余量充足,inode 未逼近上限 排除
内存耗尽被 OOM 无 OOM 记录,仍有数百 MB 可用 排除
出口网络慢(下载插件包) 下载同一个包耗时正常(秒级) 排除
网关超时 错误日志无 504/499 排除
目录权限 站点文件属主与 PHP-FPM 运行用户一致 排除
升级临时目录残留 wp-content/upgrade/ 只剩 index.php,无 .maintenance 排除

网络那条特别值得先测:如果下载占了 5 分钟,会误以为是网络问题。这里 curl -w '%{time_total}' 一次就排除了。

3.2 定位到关键日志

WordPress 的升级错误在 PHP 层,先看 php-fpm 的 PHP 错误日志(该日志路径由 pool 配置里的 php_admin_value[error_log] 指定):

/var/log/php-fpm/www-error.log

里面有一条,时间戳与操作时刻完全吻合(02:00:07 UTC = 北京时间 10:00:07):

[10-Nov-2026 02:00:07 UTC] PHP Fatal error:  Allowed memory size of 268435456 bytes exhausted
(tried to allocate 2097152 bytes) in /var/www/wordpress/wp-admin/includes/class-pclzip.php on line 4250

这一行就定性了:268435456 = 256MB,class-pclzip.php = 纯 PHP 的 unzip 实现。
文章来源:https://www.codelast.com/

3.3 出错代码

// wp-admin/includes/class-pclzip.php 约 4249-4257 行
$v_data = @fread($this->zip_fd, $p_entry['compressed_size']);  // ← 4250 行,这里要 2MB 但内存已满
$p_string = @gzinflate($v_data);                               // 解压后的数据同样全在内存里

PclZip 的工作方式是:把压缩包读进内存 → 逐条目 gzinflate → 结果继续留在内存 → 再写盘。内存峰值 ≈ 压缩包大小 + 单条目解压后大小 + 各种副本。
文章来源:https://www.codelast.com/

3.4 三个数字凑齐了因果链

事实 数值 怎么查的
Gutenberg 压缩包大小 50,295,819 字节(48MB) curl -O 后 ls -l
解压后插件目录 238MB(升级后 24.3.1 为 279MB) du -sh
PHP 内存上限 php.ini 写 128M,但 WP 后台会抬到 256M 见下

为什么是 256M 而不是 128M:wp-admin/admin.php 会调用 wp_raise_memory_limit('admin'),把上限提到 WP_MAX_MEMORY_LIMIT(默认 '256M')。所以后台操作实际可用 256M。

为什么没走 ZipArchive:

$ rpm -qa | grep -i php | grep -i zip   # 包列表里没有 zip 扩展
$ php -r 'var_dump(class_exists("ZipArchive"));'
bool(false)

WordPress 的 unzip_file() 优先用 ZipArchive(C 实现的 libzip),只有在 ZipArchive 不存在时才回退到 PclZip。这我的机器上恰好没装 zip 扩展,于是走了最耗内存的那条路。
文章来源:https://www.codelast.com/

4、根本原因

PHP 缺 zip 扩展(无 ZipArchive)
        ↓
WordPress 回退到纯 PHP 的 PclZip 解压
        ↓
Gutenberg 包 48MB,解压后 238MB,PclZip 全程在进程内存里堆
        ↓
撞穿 256MB(WP_MAX_MEMORY_LIMIT 默认值)
        ↓
PHP Fatal → 请求中断 → 浏览器只看到半截页面
        ↓
表现为「升级卡死、没有任何错误信息」

注意区分这里的两个"内存":爆掉的是 PHP 单进程的 memory_limit,不是物理内存,所以加大 swap 或加大物理内存都救不了它。这也是后来没有去改 wp-config.php 里 WP_MAX_MEMORY_LIMIT 的原因——治本要装扩展。

5、修复

5.1 安装 zip 扩展

yum install -y php-pecl-zip

注意事项:

  • 包名要与当前 PHP 版本匹配(常见形式为 php-pecl-zip 或 php<版本>-php-pecl-zip),版本号段不匹配则不会生效。
  • 该扩展一般在发行版自带的 PHP 扩展仓库里就能装到,安装它不会改变 PHP 本体版本。

安装后自动生成:

<PHP 扩展目录>/zip.so
<PHP 配置目录>/xx-zip.ini

5.2 重启 PHP-FPM

systemctl restart php-fpm.service

5.3 验证扩展真的进了 FPM 进程

CLI 能加载不等于 FPM 能加载,必须验证进程内实际映射了 zip.so:

MASTER=$(pgrep -f 'php-fpm: master process' | head -1)
grep 'php/modules/zip.so' /proc/$MASTER/maps
# 7f7ba4d8b000-... r-xp ... <PHP 扩展目录>/zip.so

master 和 worker 都能看到,说明新 fork 的 worker 必然带 ZipArchive。
文章来源:https://www.codelast.com/

6、验证

6.1 隔离实测

在 /tmp 里用完全相同的 256M 上限解同一个包:

cd /tmp && curl -O https://downloads.wordpress.org/plugin/gutenberg.24.2.0.zip
php -d memory_limit=256M -r '
  $z = new ZipArchive;
  $z->open("/tmp/gutenberg.24.2.0.zip");
  echo "entries=" . $z->numFiles . "\n";
  echo "extract=" . ($z->extractTo("/tmp/gz-test") ? "OK" : "FAIL") . "\n";
  $z->close();
  echo "php_peak=" . round(memory_get_peak_usage(true)/1048576,1) . "MB\n";
'

结果:

entries=2555
extract=OK
php_peak=2MB
real 1.661s

对照实验在同一份插件包上进行,并将 PHP 内存上限同样设为 256MB。改造前,解压过程因内存耗尽而中断;改造后,解压于 1.66 秒内完成,PHP 进程的内存峰值仅为 2MB,二者相差约两个数量级。
文章来源:https://www.codelast.com/

6.2 实际升级操作验证结果

后台重新升级,Gutenberg 24.2.0 → 24.3.1 成功。日志侧证据:

php-fpm 错误日志零新增

grep -c 'Allowed memory size'      → 1   (仍是 10:00 那次旧的)
grep -c 'Maximum execution time'   → 0

nginx 访问日志时间线

10:10:40  POST /wp-admin/update-core.php?action=do-plugin-upgrade   200  42699B
10:10:41  GET  /wp-content/plugins/...?ver=1794276640               200        ← 页面已重载
10:10:47  GET  /wp-admin/update.php?action=update-selected&plugins=gutenberg%2Fgutenberg.php  200

从提交到页面刷新 1~3 秒(原先卡 5 分钟后静默失败)。
文章来源:https://www.codelast.com/

7、一句话总结

WordPress 后台「升级卡住、没有任何报错」这类问题,先去看 php-fpm 的 www-error.log,一行 Allowed memory size ... class-pclzip.php 就能定性:服务器缺 php-zip 扩展,yum install php-pecl-zip + 重启 FPM 即可。
文章来源:https://www.codelast.com/

文章来源:https://www.codelast.com/
➤➤ 版权声明 ➤➤ 
转载需注明出处:codelast.com 
感谢关注我的微信公众号(微信扫一扫):
wechat qrcode of codelast
以及我的微信视频号:

发表评论