yii::$app->queue->push() 必须在响应发出后调用,否则阻塞http导致白屏;推荐放afteraction钩子中,并序列化必要上下文字段。

Yii::$app->queue->push() 调用必须放在控制器动作末尾或响应已发出之后,否则 HTTP 响应会被阻塞,用户看到“白屏”或超时——这是 RESTful 接口集成队列最常踩的坑。
RESTful 控制器里 push 任务前要先返回响应
Yii 的 RESTful 控制器(如 ActiveController)默认会等整个 action 执行完才输出 JSON。如果你在 actionCreate() 里直接 Yii::$app->queue->push($job) 后没做任何处理,用户请求就卡在那儿了,直到任务执行完(实际不会执行,只是排队等待 worker),但用户根本不知道。
- 正确做法是:先调用
Yii::$app->response->send()或显式return $this->asJson([...]),再 push 任务 - 更稳妥的方式是把
push()放到afterAction()钩子中,确保响应已发出 - 如果任务依赖当前请求上下文(比如
Yii::$app->user->id),记得在 push 前把必要字段序列化进$job,别传闭包或未序列化对象
yii\queue\db\Queue 在高并发 REST 场景下的性能瓶颈
数据库表 {{%queue}} 在每秒几十次 push 时,容易因 INSERT ... SELECT FOR UPDATE 和 mutex 行锁导致排队延迟。你看到日志里 Failed to acquire mutex 或任务延迟数秒入队,大概率是这个原因。
- 避免在
web.php中配置'mutex' => \yii\mutex\Mysql::class;改用\yii\mutex\RedisMutex(需装yiisoft/yii2-redis) -
tableName别用默认的{{%queue}},加前缀隔离(如'tableName' => '{{%queue_api}}'),减少与其他队列组件争抢 - 如果只是轻量通知类任务(如发短信、写日志),考虑改用
yii\queue\sync\Queue(同步执行,无延迟),而不是硬上 DB 队列
如何让 REST 接口返回任务 ID 并支持状态查询
用户调用 POST /api/orders 后只收到 {"status":"accepted","job_id":"12345"},但后端还没真正处理——这需要任务类自己生成可追踪 ID,并存到独立状态表,不能依赖队列内部的 id 字段(它只在 worker 拉取时才稳定)。
- 在任务类构造时生成唯一
$this->jobId = Yii::$app->security->generateRandomString(12) - push 前先插入一条
job_status记录:JobStatus::create(['id'=>$job->jobId, 'status'=>'pending']) - 重写
execute(),开头更新状态为running,结尾更新为done或failed - 提供
GET /api/jobs/{id}接口查JobStatus::findOne($id),而非查队列表
console.php 里 queue/run 命令启动失败的常见原因
运行 php yii queue/run 报错 Class 'yii\queue\db\Queue' not found 或 SQLSTATE[42S02]: Base table or view not found,不是代码写错了,而是环境没对齐。
- 确保
console.php也配置了'queue'组件(很多项目只在web.php配,worker 启动时根本找不到类) - 执行
php yii queue/listen前,先手动建表:php yii migrate --migrationPath=@yii/queue/migrations - 如果用 Redis,检查
console.php是否加载了redis组件,且'redis' => ['class' => 'yii\redis\Connection']配置完整(host/port/database 缺一不可)
jobId 的生成时机、状态存储位置、worker 启动环境这三个点最容易被忽略,一漏就变成“假异步”。











