结论:用 enqueue/enqueue,别自己封装抽象层;因其不强求统一语义,而是通过transport层收敛差异,暴露一致的context/producer/consumer接口,同时支持按需深入特定后端控制。

直接上结论:用 enqueue/enqueue,别自己封装抽象层。
为什么不是自定义抽象接口?
自己写一个统一的 QueueInterface 听起来干净,但实际会快速失控——RabbitMQ 的 exchange、routing key、ack 机制,Kafka 的 partition、offset、consumer group,Redis 的 BRPOP 阻塞时长与重试逻辑,根本没法用一套方法签名覆盖。硬套会导致要么功能阉割(比如丢掉死信、延迟、优先级),要么参数爆炸(每个 push() 方法塞七八个可选参数)。
用 enqueue/enqueue 的好处是它不假装“所有队列都一样”,而是把差异收进 transport 层,暴露一致的 Context、Producer、Consumer 接口,同时允许你随时切到具体 transport 做深度控制。
如何用 Composer 安装并启用多后端支持?
执行安装命令即可拉取核心包和常用 transport:
composer require enqueue/enqueue:^0.10 enqueue/amqp-lib:^0.10 enqueue/redis:^0.10 enqueue/kafka:^0.10
注意版本号必须对齐(如全用 ^0.10),否则类名冲突或方法缺失。不要只装 enqueue/enqueue,它本身不带任何 transport 实现。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
-
enqueue/amqp-lib对应 RabbitMQ(需 php-amqplib 扩展) -
enqueue/redis依赖 php-redis 扩展,用 list + pub/sub 模拟队列 -
enqueue/kafka依赖 rdkafka 扩展,不支持纯 PHP 实现
装完后,在初始化时按环境变量切换 transport:
$transport = $_ENV['QUEUE_TRANSPORT'] ?? 'redis';
$context = match($transport) {
'amqp' => (new \Enqueue\AmqpLib\AmqpConnectionFactory())->createContext(),
'redis' => (new \Enqueue\Redis\RedisConnectionFactory())->createContext(),
'kafka' => (new \Enqueue\Kafka\KafkaConnectionFactory())->createContext(),
};
生产环境配置容易漏掉的关键点
常见错误不是连不上,而是消息丢了或卡住:
- RabbitMQ 场景下,忘记在
AMQPMessage中设'delivery_mode' => 2,导致 broker 重启后消息丢失 - Redis 场景下,没配
'redis' => ['default' => ['scheme' => 'tcp', 'host' => '127.0.0.1', 'port' => 6379]],连接失败却不报错,静默降级成内存队列 - Kafka 场景下,
'group_id'写死成固定值,多个 worker 实例变成竞争消费同一 partition,而不是负载均衡 - 所有 transport 都要显式调用
$context->close(),否则 PHP-FPM 进程常驻时连接泄漏,Redis 连接数爆满
怎么让不同服务用不同队列而不改代码?
靠运行时传入不同的 topic 和 queue 名,而不是靠 if-else 切 transport:
$producer = $context->createProducer();
$message = $context->createMessage('payload');
$producer->send($context->createTopic('order.created'), $message); // 发给 topic
$producer->send($context->createQueue('sms.send'), $message); // 发给 queue
关键在于:createTopic() 和 createQueue() 返回的对象由当前 transport 决定语义——RabbitMQ 下 topic 映射 exchange,Redis 下 topic 就是 list key,Kafka 下 topic 是原生 topic。你不用管底层,但必须清楚业务语义:事件广播用 topic,点对点任务用 queue。
最易被忽略的是 transport 初始化时机和生命周期管理——它不该在每次 HTTP 请求里 new 一次,而应作为容器服务单例复用;更不能在 CLI worker 里用完不 close,否则连接池撑爆 Redis 或 Kafka broker。










