PHP队列消费进程卡住需先检查pcntl_fork是否被禁用;AI请求须设硬超时;Redis队列应使用BRPOPLPUSH防消息丢失;systemd需调大MemoryLimit和RestartSec。

PHP队列消费进程卡住不动?先查pcntl_fork是否被禁用
很多线上环境(尤其是共享主机、Docker默认配置、部分云函数)会禁用pcntl_fork,而基于子进程的队列消费者(比如用while(true) + pcntl_fork实现的多工单处理)一启动就静默退出,日志里连错误都不报。
实际表现是:php worker.php执行后立刻返回,进程列表里找不到它,但也没有报错。
- 用
php -r "var_dump(function_exists('pcntl_fork'));"确认是否可用 - 禁用时别硬改,换
amqp扩展 +RabbitMQ或redis的BRPOP阻塞模式更稳妥 - 如果必须用
pcntl,检查disable_functions配置项是否含pcntl_fork,pcntl_waitpid等
AI请求超时崩掉整个队列?必须给file_get_contents或cURL设硬超时
批量调AI接口最常见翻车点:某个请求卡在TLS握手或响应慢,导致当前worker进程挂起,后续任务全堵住。PHP默认的file_get_contents没有内置超时控制(除非显式配stream_context_create),cURL也常漏设CURLOPT_TIMEOUT_MS。
-
file_get_contents必须搭配stream_context_create,关键参数:'timeout' => 8(单位秒)、'http' => ['method' => 'POST'] -
cURL至少设三个超时:CURLOPT_TIMEOUT_MS(总耗时)、CURLOPT_CONNECTTIMEOUT_MS(建连)、CURLOPT_LOW_SPEED_LIMIT防假死 - 别信“AI服务稳定”,实测OpenAI/千问/文心在高峰时段
503或429响应延迟可达30s+
Redis队列用LPUSH + BRPOP就行?小心并发漏消息
看似简单的LPUSH推任务、BRPOP拉任务,在高并发写入+多worker消费时,可能因网络抖动或进程信号中断,导致某条任务被BRPOP取走但没来得及处理就崩溃,消息永久丢失。
- 必须用
BRPOPLPUSH代替BRPOP,把任务先挪到processing:queue暂存链表,处理完再LREM或LTRIM - 加
EXPIRE给processing链表设10分钟过期,避免worker宕机后任务卡死 - 别用
DEL清空队列——LRANGE+LTRIM更安全,避免清空过程中新任务插入被误删
为什么systemd管理PHP队列进程老被杀?盯紧MemoryLimit和RestartSec
用systemd跑PHP长进程,看似规范,但默认MemoryLimit=512M对批量AI请求极不友好——一次json_encode大响应体、或curl_exec缓存未清,内存就飙到600M+,systemd直接Kill进程且不记OOM日志。
- 在service文件里明确写
MemoryLimit=1G(根据单次AI响应大小估算,通常512M不够) -
RestartSec=5必须设,否则连续崩溃会触发StartLimitIntervalSec限流,worker彻底停摆 - 加
Environment="PHP_MEMORY_LIMIT=1G",双保险防止PHP内核自己Allowed memory size exhausted
真正麻烦的是状态同步——AI请求失败后,要重试还是丢弃,靠Redis原子操作很难覆盖所有中断场景。建议每个任务带retry_count字段,消费前INCR,失败时DECR回滚,比纯队列逻辑更可控。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











