Swoole任务队列必须依附于已启动的Swoole HTTP/TCP Server,CLI中调用Queue::push()无效,因task_worker未运行;其本质是Server的onTask回调机制封装,非独立队列服务。

Swoole 的任务队列不是“开箱即用”的 Laravel Queue 替代品,它本质是 task 进程模型的封装,必须配合 Server 生命周期使用——没启 Swoole HTTP/Server 实例,task 就根本不会运行。
为什么 Queue::push() 在 CLI 命令里不触发任务执行
这是最常踩的坑:Laravel-Swoole 的 SwooleTaskQueue 依赖 Swoole 主服务的 onTask 回调,而该回调只在 Worker 进程收到 $server->task() 调用时才被调度。CLI 命令(如 php artisan queue:work)走的是传统 PHP-FPM 或命令行模式,task_worker 根本未启动,所有 push 只是写进内存队列,然后静默丢弃。
- ✅ 正确做法:只在 Swoole HTTP 或 TCP Server 启动后,由 Web 请求或协程内投递任务(例如控制器中调用
Queue::push(new SendEmailJob())) - ❌ 错误做法:在 Artisan 命令、定时任务脚本、或
php -r中直接调用Queue::push()并期待后台执行 - ⚠️ 验证方式:查看
swoole_http.php中'task_worker_num' => 4是否生效,并用ps aux | grep task确认有 task 进程存在
$server->task() 和 Queue::push() 的底层关系
Queue::push() 最终会调用 Swoole Server 实例的 task() 方法,但前提是这个 Server 实例已被绑定到 Laravel-Swoole 的生命周期中。它不是独立进程,而是依附于主 Server 的子系统。
-
Queue::push()是 Laravel 兼容层,自动序列化 Job、设置默认延迟、走dispatch流程 -
$server->task($data)是原始 Swoole API,需手动序列化、无重试、无失败回调,适合极简场景(如日志异步落盘) - 性能差异:直接
task()略快(少一层抽象),但失去 Laravel 的failed()、retryAfter()、中间件等能力 - 注意:若 Job 中用了
DB::connection()或Redis::get(),必须确保这些连接在 task 进程中可复用(推荐用DB::reconnect()或协程客户端)
延迟任务 later() 为什么有时不准
Swoole 的 task 不原生支持定时,Queue::later(60, $job) 实际是把任务先存进内存队列,再靠 Manager 进程每秒轮询触发。这意味着它不是高精度定时器,尤其在负载高、task worker 忙碌时,可能延迟数秒甚至更久。
- ✅ 适用场景:邮件发送(±5 秒误差可接受)、通知推送、非关键数据同步
- ❌ 不适用场景:金融对账、实时风控、需要毫秒级触发的业务
- 替代方案:真定时需求建议用
Swoole\Timer::after()+$server->task()组合,或接入 Redis ZSET + 单独 timer 进程 - 配置影响:
'task_worker_num'过低会导致轮询积压,建议设为swoole_cpu_num() * 2,而非固定值 1 或 2
任务失败后,结果怎么拿?
Swoole 的 task 模型天然支持结果返回:Worker 执行完调用 $server->finish($result),主线程在 onFinish 回调中接收。但 Laravel-Swoole 默认关闭了这个通道——它把 finish 当作“执行完成”信号,不反序列化或透传结果。
- 要获取结果,必须手动注册
onFinish回调,并用唯一 ID 关联请求与响应(例如把task_id存入 Redis,onFinish时更新) -
onFinish是全局回调,不能按 Job 类型区分处理,需自行路由逻辑 - 切勿在
onFinish中做耗时操作(如 DB 写入),否则阻塞整个 Server 事件循环;应只发通知或写轻量缓存 - 一个更稳的路子:任务成功后主动调用 Webhook 或发 Redis Pub/Sub,让其他服务监听消费
真正难的从来不是“怎么投递”,而是“怎么确认它跑完了、跑对了、跑得及时”。Swoole 任务队列省掉的是进程管理开销,不是业务可靠性设计——这部分,还得自己一关一关补上。










