必须用 queue:work amqp 而非 queue:listen,因后者轮询 fork 进程、无法复用 AMQP channel,导致频繁重连、限流、CPU 暴涨,且不支持重试控制参数。

必须用 php think queue:work amqp 启动,不能用 queue:listen;Supervisor 配置里 command 必须写绝对路径、directory 必须指向项目根目录,否则进程起不来或找不到配置。
为什么不能用 queue:listen 启动 RabbitMQ 消费者
queue:listen 是轮询式监听,每次有新任务就 fork 一个新 PHP 进程,不复用实例。RabbitMQ 场景下它无法建立长连接通道(AMQP channel),频繁重连会触发 broker 限流甚至断连;更关键的是,它不支持 --tries、--sleep 等失败控制参数,异常时只会无限重试,导致 CPU 暴涨。而 queue:work amqp 复用单个连接和 channel,启动后持续 consume,符合 AMQP 协议设计本意。
常见错误现象:
- Supervisor 日志里反复出现 Connection refused 或 Channel closed
- supervisorctl status 显示进程在 STARTING 和 BACKOFF 之间反复横跳
- RabbitMQ 管理后台显示大量短生命周期的 connection,但 consumer count 为 0
Supervisor 配置中哪些字段最容易填错
以下字段必须严格按生产环境要求填写,少一个就可能启动失败:
-
command:必须是完整绝对路径,例如/usr/bin/php /var/www/myapp/think queue:work amqp --sleep=3 --max-jobs=500 --tries=3;写成php think queue:work...会导致 supervisor 在默认 PATH 下找不到php或think -
directory:必须设为项目根目录(即含think文件和config/queue.php的目录),否则加载不到队列驱动配置 -
environment:如果 RabbitMQ 使用了 TLS 或自定义端口,需显式传入,例如environment=AMQP_HOST="127.0.0.1",AMQP_PORT="5671",AMQP_SSL="true" -
autorestart:建议设为unexpected,避免因正常退出(如--max-jobs触发)被误重启
think-queue-amqp 驱动的关键配置项
ThinkPHP6 使用 RabbitMQ 必须安装 topthink/think-queue-amqp,而非基础版 think-queue。它的配置集中在 config/queue.php 的 amqp 驱动段:
立即学习“PHP免费学习笔记(深入)”;
-
host、port、login、password、vhost必须与 RabbitMQ 实际部署一致;vhost默认是/,但 URL 编码后要写成%2F -
queue名称不要带空格或特殊字符,否则声明队列时会报PRECONDITION_FAILED -
exchange类型推荐direct,配合 routing_key 使用;若用topic,确保 consumer 绑定的 pattern 能匹配上 producer 发送的 key -
retry_after不生效 —— AMQP 驱动靠 RabbitMQ 自身的requeue和dead-letter机制处理失败,不是靠这个参数
验证是否加载成功:运行 /usr/bin/php /var/www/myapp/think queue:work amqp --once,能正常消费一条任务且无 PHP Fatal 错误,说明驱动和配置 OK。
Supervisor 启动后怎么确认 RabbitMQ 消费者真正在 work
不能只看 supervisorctl status 显示 RUNNING 就认为没问题。要交叉验证三层状态:
- Supervisor 层:
supervisorctl tail -f test_queue stderr查看是否有Connected to AMQP或Declaring queue日志;若只有Starting没后续,大概率是directory或command路径错了 - RabbitMQ 层:访问
http://your-rabbitmq:15672→ “Queues” 页面 → 找到对应队列 → 看 “Consumers” 列是否 ≥ 1;点进去看 “Channels” 是否处于running状态 - 应用层:在消费者类的
handle()方法开头加一行file_put_contents('/tmp/consume.log', date('Y-m-d H:i:s') . "\n", FILE_APPEND);,观察日志是否随任务入队而持续追加
最常被忽略的一点:RabbitMQ 的 consumer_timeout 默认是 30 分钟,如果消费者进程卡住没发 heartbeat,broker 会主动 close channel。所以 --sleep=3 不仅控重试间隔,也间接保证了心跳频率 —— 这个细节在文档里几乎不提,但线上出问题十次有八次跟它有关。



















