直接选 yii\queue\amqp_interop\Queue——它是Yii2官方推荐、维护活跃、兼容PHP 8+和RabbitMQ 3.10+的AMQP驱动,旧版yii\queue\amqp\Queue已停更且依赖过时库,易在TLS、vhost或SASL认证上失败。

用 yii\queue\amqp_interop\Queue 还是 yii\queue\amqp\Queue?
直接选 yii\queue\amqp_interop\Queue ——这是 Yii2 官方推荐、维护更活跃、兼容 PHP 8+ 和 RabbitMQ 3.10+ 的 AMQP 驱动。旧版 yii\queue\amqp\Queue 已基本停更,且依赖过时的 enqueue/amqp-lib,容易在 TLS 连接、vhost 路径或 SASL 认证上失败。
常见错误现象:Connection refused 或 AMQP protocol error: FRAME_END missing,往往是因为驱动底层协议栈不匹配。
- 必须用
composer require yiisoft/yii2-queue+composer require enqueue/amqp-lib(注意不是php-amqplib) -
enqueue/amqp-lib是纯 PHP 实现,不依赖amqp.so扩展,部署更轻量 - 若你已装了
amqp.so,反而要禁用它,否则可能触发冲突(如Class 'AMQPConnection' not found)
'bootstrap' => ['queue'] 必须加,但只对 console 有效
这个配置让队列组件在控制台应用启动时自动注册监听器,否则 yii queue/listen 命令根本不会响应任何任务 —— Web 请求里加它没用,也别加。
使用场景:只有在 console/config/main.php 里配才生效;web/config/main.php 中配置只会浪费内存,且可能引发 Cannot use object of type yii\queue\amqp_interop\Queue as array 类错误。
-
'bootstrap' => ['queue']要放在console/config/main.php的'components'同级位置 - 确保
console/controllers/QueueController.php存在(yii2-queue 自带),否则yii queue/listen命令无法识别 - 如果用 Supervisor 管理消费者进程,命令必须是
php yii queue/listen --verbose,不能漏掉--verbose,否则失败时不报错也不退出
ttr 和 attempts 不是“重试次数”,而是“单次最长执行时间 + 全局失败阈值”
很多人以为 attempts => 3 表示失败后立刻重试 2 次,其实不是:它表示该任务最多被投递 3 次(含首次),每次失败后默认延迟约 1 秒再入队 —— 这个行为由驱动内部实现,不可配置间隔。
更关键的是 ttr(Time To Run):它限制的是单次 execute() 方法执行的**最大耗时**,超时会强制终止进程并标记为失败,然后计入 attempts 计数。若任务本身需要 6 分钟处理,却设 ttr => 300(5 分钟),就会被粗暴 kill,且无法恢复上下文。
-
ttr单位是秒,建议设为实际业务逻辑平均耗时的 1.5–2 倍,但不要超过 RabbitMQ 的heartbeat(默认 60s),否则连接可能被服务端断开 -
attempts设为 1 表示“只试一次,失败即丢弃”,适合日志类、通知类等幂等性弱的任务 - 真正需要可靠重试的场景(如支付回调),应配合死信队列(DLX)+ 延迟插件,而不是靠
attempts硬扛
消息体序列化默认用 igbinary,但你大概率要用 json
yii2-queue 默认用 igbinary 序列化任务对象,速度快、体积小,但有个致命问题:它把类名和属性名一起编码,一旦你改了 Job 类名或字段名,所有未消费的老任务都会反序列化失败,报 unserialize(): Error at offset。
真实项目里,Job 类频繁迭代、团队协作多、上线节奏快 —— 此时 JSON 是更安全的选择,哪怕慢一点、大一点,也比批量卡死强。
- 在 queue 组件配置中加
'serializer' => \yii\queue\serializers\JsonSerializer::class - JSON 序列化要求 Job 类所有 public 属性可 JSON 化(不能含 resource、Closure、PDO 实例等)
- 若 Job 构造函数参数较多,建议用数组传参(
new SendEmailJob(['to' => 'a@b.c', 'template' => 'welcome'])),而非裸对象传参,避免反序列化歧义
最常被忽略的一点:RabbitMQ 的 queueName 不是“随便起个名字就行”。它本质是 AMQP 的 queue name,受服务端字符限制(如不能含 /、空格、控制字符),且大小写敏感;如果多个环境共用一个 RabbitMQ 实例,务必在名称里带上环境前缀,比如 prod_send_email,否则开发机一跑 yii queue/listen,就把线上队列里的任务全抢走了。










