PHP长时脚本触发后不阻塞请求的多环境异常排查
PHP长时脚本后台运行跨环境问题排查
问题背景
编写了一段PHP长时脚本,期望触发后不阻塞后续请求,脚本代码如下:
// filename = htdocs/api/generateFiles.php ob_end_clean(); header("Connection: close\r\n"); header("Content-Encoding: none\r\n"); ignore_user_abort(true); // optional set_time_limit(1*3600); // 1 hora max ob_start(); $filepath = DOL_DOCUMENT_ROOT . '/exports/export-' . date('Ymd-his'); if (!file_exists($filepath)) { mkdir($filepath, 0777, true); } $sql = "INSERT INTO export_process(folder,fk_user) VALUES ('" . $filepath . "', {$user->id});"; $resSql = $db->query($sql); if (!$resSql) { http_response_code(500); // Internal Server Error echo json_encode(['error' => $db->lasterror()]); $size = ob_get_length(); header("Content-Length: $size"); ob_end_flush(); // Strange behaviour, will not work flush(); // Unless both are called ! ob_end_clean(); exit(); } echo json_encode(['success' => true]); $size = ob_get_length(); header("Content-Length: $size"); ob_end_flush(); // Strange behaviour, will not work flush(); // Unless both are called ! ob_end_clean(); session_write_close(); // Here goes the long running export function // Every completed step updates the inserted export_process to increase the completion percentage
三个运行环境表现差异:
- 本地环境(Windows 11 + XAMPP):AJAX调用后可立即浏览网站,脚本后台运行正常;
- 测试环境(QNAP NAS Linux):AJAX调用后加载动画持续到脚本结束才显示结果,手动关闭标签页后新标签页仍需等待脚本结束;
- 生产环境(QNAP NAS Linux):AJAX调用后页面立即刷新,但脚本中途终止(日志显示50步仅执行到第33步)。
已做配置:ignore_user_abort(true)、output_buffering = 4096、session_write_close(),测试与生产环境配置、.htaccess完全一致,但问题仍存在。
可能的排查方向
1. Web服务器层面的输出缓冲/压缩
- 检查QNAP上Apache是否开启了
mod_deflate或mod_gzip,服务器端的强制压缩会忽略脚本中Content-Encoding: none的设置,导致输出无法及时flush到客户端。可以在脚本开头添加apache_setenv('no-gzip', 1);,或者在该脚本所在目录的.htaccess中加入SetEnv no-gzip 1禁用压缩。 - Apache自身的
BufferSize或OutputBufferSize配置可能覆盖PHP的输出缓冲设置,导致输出无法立即发送。可以修改Apache的站点配置,针对该脚本路径关闭输出缓冲。
2. PHP运行模式与进程限制
- 如果QNAP的PHP采用FastCGI/php-fpm模式运行,检查
php-fpm.conf中的request_terminate_timeout参数,生产环境可能设置了较短的超时时间,导致脚本中途被终止,建议将其调整为3600秒以上。同时检查pm.max_requests,避免脚本运行中进程被重启。 - QNAP NAS系统可能有资源监控机制,当脚本占用CPU/内存过高时会被强制终止,查看系统日志(如
/var/log/messages或QTS后台日志)是否有进程被kill的记录。
3. 数据库连接与锁问题
- 长时脚本中的数据库操作可能持有行锁或表锁,导致后续请求被阻塞。检查
export_process表的更新逻辑,确保没有不必要的事务或锁;每完成一步数据库操作后及时commit(若使用事务),避免长时间持有锁。 - 检查MySQL的
wait_timeout参数,如果脚本运行时间超过该值,数据库连接会被断开导致脚本终止。可以在长时脚本中定期执行$db->ping()保持连接,或者调大wait_timeout的值。
4. 文件系统权限与IO瓶颈
- 生产环境的
/exports目录看似设置了0777权限,但QNAP的文件系统可能有ACL权限限制,导致Apache运行用户(如www-data)实际无读写权限,查看系统日志是否有权限错误记录。 - 生产环境磁盘IO负载过高,导致脚本的文件操作阻塞甚至失败,用
iostat命令对比测试与生产环境的磁盘读写性能,排查是否存在IO瓶颈。
5. 网络连接保持设置
- QNAP的防火墙或网络服务可能开启了连接保持机制,导致服务器不会及时关闭与客户端的连接,进而阻塞后续请求。检查NAS的网络配置,调整连接超时参数。
内容的提问来源于stack exchange,提问作者Daviid
相关产品推荐
相关产品推荐

