yii 3.0 中需手动集成 php-amqplib v3.6+ 实现 rabbitmq 异步任务,配置连接参数、编写可序列化任务类、封装发布/消费逻辑,并通过 di 容器注入连接实例启动消费者。

在 Yii 3.0 中启用 RabbitMQ 异步任务,必须绕过框架默认的同步执行模型,手动集成 AMQP 客户端并构建可调度、可重试、带事务边界的任务生命周期——这不像 Yii 2.x 那样有内置 queue 组件,也没有 yiiqueuerabbitmq 扩展可用。
安装 RabbitMQ 客户端与依赖
执行 composer 命令安装官方推荐的 amqp 扩展:composer require php-amqplib/php-amqplib。
该扩展是 PHP 生态中事实标准的 AMQP 1.0 实现,支持 RabbitMQ 3.10+ 及所有主流插件(如 shovel、federation),【不兼容旧版 php-amqplib v2.x】,务必确认安装的是 v3.6+ 版本。
验证安装:运行 php -r "echo class_exists('PhpAmqpLib\Connection\AMQPStreamConnection') ? 'OK' : 'FAIL';",输出 OK 即表示扩展加载成功。
配置 RabbitMQ 连接参数
在 config/common.php 或独立的 config/rabbitmq.php 中定义连接配置:
[ 'rabbitmq' => [ 'host' => $_ENV['RABBITMQ_HOST'] ?? 'localhost', 'port' => (int)($_ENV['RABBITMQ_PORT'] ?? 5672), 'user' => $_ENV['RABBITMQ_USER'] ?? 'guest', 'password' => $_ENV['RABBITMQ_PASS'] ?? 'guest', 'vhost' => $_ENV['RABBITMQ_VHOST'] ?? '/', 'exchange' => 'default.exchange', 'queue' => 'default.queue', ] ]
注意:vhost 必须已由管理员创建并授权给指定用户,否则连接会静默失败;【RabbitMQ 默认 vhost 是 /,但生产环境严禁使用 guest/guest 登录】。
编写可序列化的任务类
创建 src/Queue/Task/SendEmailTask.php:
namespace AppQueueTask; use YiisoftSerializerSerializerInterface; use YiisoftDiContainer; final class SendEmailTask { private string $to; private string $subject; private string $body; public function __construct(string $to, string $subject, string $body) { $this->to = $to; $this->subject = $subject; $this->body = $body; } public function execute(Container $container): void { $mailer = $container->get(AppMailer::class); $mailer->send($this->to, $this->subject, $this->body); } }
这个类必须满足三个硬性条件:① 构造函数参数全为标量或可序列化对象;② 不含闭包、资源、PDO 实例等不可序列化成员;③ execute 方法接收 Container 实例而非全局 Yii::$app。
封装消息发布与消费逻辑
方法一:手写 Publisher 类
在 src/Queue/Publisher.php 中实现:
use PhpAmqpLibConnectionAMQPStreamConnection; use PhpAmqpLibMessageAMQPMessage; use YiisoftSerializerJsonSerializer; final class Publisher { private AMQPStreamConnection $connection; private JsonSerializer $serializer; public function __construct(AMQPStreamConnection $connection, JsonSerializer $serializer) { $this->connection = $connection; $this->serializer = $serializer; } public function publish(object $task): void { $channel = $this->connection->channel(); $channel->queue_declare('default.queue', false, true, false, false); $data = $this->serializer->serialize($task); $msg = new AMQPMessage($data, [ 'delivery_mode' => 2, ]); $channel->basic_publish($msg, '', 'default.queue'); $channel->close(); } }
方法二:用 DI 容器自动注入连接实例
在 config/di.php 中绑定:
return [ PhpAmqpLibConnectionAMQPStreamConnection::class => [ 'class' => PhpAmqpLibConnectionAMQPStreamConnection::class, '__construct()' => [ $_ENV['RABBITMQ_HOST'], (int)$_ENV['RABBITMQ_PORT'], $_ENV['RABBITMQ_USER'], $_ENV['RABBITMQ_PASS'], $_ENV['RABBITMQ_VHOST'], ], ],];
这样 Publisher 构造时就能自动获得已配置的连接实例,避免每次手动 new。
启动消费者进程
第一步:创建 bin/consume-rabbitmq 可执行脚本(chmod +x):
#!/usr/bin/env php <?php require __DIR__ . '/../vendor/autoload.php'; use AppQueueConsumer; use YiisoftDiContainer; $container = new Container(require __DIR__ . '/../config/di.php'); $consumer = $container->get(Consumer::class); $consumer->run();
第二步:在 src/Queue/Consumer.php 中实现监听循环:
use PhpAmqpLibConnectionAMQPStreamConnection; use PhpAmqpLibChannelAMQPChannel; use YiisoftSerializerJsonSerializer; use YiisoftDiContainer; final class Consumer { private AMQPStreamConnection $connection; private JsonSerializer $serializer; private Container $container; public function __construct(AMQPStreamConnection $connection, JsonSerializer $serializer, Container $container) { $this->connection = $connection; $this->serializer = $serializer; $this->container = $container; } public function run(): void { $channel = $this->connection->channel(); $channel->queue_declare('default.queue', false, true, false, false); $callback = function ($msg) { try { $task = $this->serializer->unserialize($msg->getBody(), AppQueueTaskSendEmailTask::class); $task->execute($this->container); $msg->ack(); } catch (Throwable $e) { $msg->nack(); throw $e; } }; $channel->basic_consume('default.queue', '', false, false, false, false, $callback); while ($channel->is_consuming()) { $channel->wait(); } $channel->close(); $this->connection->close(); } }
第三步:在终端运行 php bin/consume-rabbitmq 启动常驻进程。该进程会持续拉取消息、反序列化、执行任务并确认应答。











