Swoole队列分三类:swMsgQueue(IPC级task投递)、CoroutineChannel(协程内通信)、Redis/AMQP(外部中间件);混用会导致任务丢失、阻塞或无法跨进程消费。

直接说结论:Swoole 的队列不是“开箱即用”的通用消息中间件,而是分场景、分层级的三类机制——swMsgQueue(IPC 级 task 投递)、SwooleCoroutineChannel(协程内轻量通信)、Redis/AMQP(外部中间件集成)。混用或误选会导致任务丢失、阻塞、无法跨进程消费。
task_worker 里的队列:只认 swMsgQueue,别手写数组或 SplQueue
你在 onTask 回调里写的逻辑,背后依赖的是 Swoole 底层基于 System V msgget/msgsnd 实现的 swMsgQueue。它不是 PHP 用户态队列,而是进程间通信(IPC)设施。
- 你调用
$server->task($data),本质是向这个 IPC 队列发一条带mtype的消息,由task_worker进程轮询接收 - 如果自己在
onTask里 new 一个SplQueue或数组存任务,那只是当前 task_worker 进程内的临时变量,重启后清空,且无法被其他 worker 共享 -
message_queue_key配置不设或设错(比如用ftok(__FILE__, 1)但文件路径变了),会导致新启动的 server 拿不到旧队列,残留任务永远卡住
协程内高频投递:用 SwooleCoroutineChannel,别用 sleep 轮询
如果你在协程里模拟“生产者-消费者”,比如日志采集器往内存队列塞数据、多个协程消费者取走处理,SwooleCoroutineChannel 是唯一推荐方式——它是无锁、协程安全、支持阻塞/非阻塞 pop/push 的环形缓冲区。
- 错误写法:
while ($mq->isEmpty()) { co::sleep(0.1); }—— 大量空转消耗 CPU,且无法响应取消信号 - 正确写法:
$channel = new SwooleCoroutineChannel(1024); $channel->push($msg); $msg = $channel->pop();,pop 默认阻塞,没消息时自动让出协程 - 容量设太小(如 16)会频繁触发
Channel is full异常;设太大(如 100000)可能吃光内存,建议按峰值 QPS × 平均处理延迟预估
要持久化、要跨机器、要重试:必须对接 Redis 或 RabbitMQ,别硬扛
Swoole 自带的队列不持久、不跨进程、不支持 ACK 和重试。一旦 worker crash 或 server 重启,IPC 队列里的任务就丢了。真要上生产,得靠外部中间件。
-
Redis方案适合中小流量:用LPUSH/BRPOP+setex做延时,配合SwooleRedis协程客户端,避免阻塞事件循环 -
RabbitMQ方案适合高可靠场景:必须用basic_qos控制 prefetch count,否则一个慢 consumer 会拖垮整个队列;投递时设delivery_mode=2才能持久化 - 切忌把
file_put_contents日志写入逻辑直接塞进onTask——磁盘 IO 会拖慢所有 task_worker,应改用异步写或转发到 Redis 队列再批量落盘
最常被忽略的一点:Swoole 的 task 队列和协程 Channel 完全不互通。你不能在 onReceive 里 push 到 Channel,再指望 onTask 里 pop 出来——它们根本不在同一个内存空间。跨模型通信,只能靠外部存储或进程间信号。










