必须将耗时操作异步化:接口仅登记任务并立即返回,真实逻辑封装为实现jobinterface的独立job类;选yii\queue\amqp_interop\queue驱动,dsn中vhost需url编码,禁用amqp.so扩展;console配置中'bootstrap'=>['queue']须置于根级;显式设置attempts=>3和ttr=>60;消费者必须以--verbose运行。

直接把耗时操作扔进队列,接口就能从几十秒降到毫秒级响应——但前提是队列配置对、任务写对、消费者跑对。Yii2.0.51本身不慢,慢的是你让Web请求等一个本不该它等的过程。
别让接口卡在同步执行上
用户上传大文件、生成报表、发批量短信这类操作,逻辑本身就该异步。如果还在Controller里直接调用fileProcessService->generateReport(),哪怕只花3秒,100个并发就压出300秒排队延迟。必须拆开:接口只做“登记”,队列负责“干活”。
- Controller立即返回任务ID和状态(如
{"taskId":"abc123","status":"QUEUED"}) - 真实处理逻辑封装成独立Job类,实现
yii\queue\JobInterface - 禁止在Job中使用
Yii::$app或未序列化的对象(如PDO连接、Redis实例)
选对驱动,避免连接静默失败
用RabbitMQ就只认yii\queue\amqp_interop\Queue。旧的amqp驱动已停更,PHP 8+下容易出现FRAME_END missing或认证卡死,根本连不上。
- DSN里的vhost必须URL编码:
amqp://guest:guest@localhost:5672/%2F(/不能写成/) - 如果本地启用了
amqp.so扩展,务必在php.ini里注释掉,否则和纯PHP实现冲突 -
'bootstrap' => ['queue']必须放在console/config/main.php根级,绝不能塞进components里
重试与超时要显式配,别信默认值
Yii2队列不会自动重试失败任务,除非你明确告诉它“最多试3次”“每次最多跑60秒”。默认attempts=0意味着失败即丢弃,ttr=300可能让一个卡死任务占着消费者进程5分钟。
-
'attempts' => 3:任务最多执行3次(首次+2次重试),第3次失败后进failed表 -
'ttr' => 60:不是延迟60秒再试,而是单次执行超60秒就强制kill并计为失败 - 任务类里别
try/catch吞异常——队列靠抛异常判断失败;静默return等于告诉系统“成功了”
消费者必须带--verbose常驻运行
用Supervisor或systemd拉起消费者时,命令必须是php yii queue/listen --verbose。漏掉--verbose,任务失败既不打堆栈也不退出进程,看起来在跑,其实已经卡死,新任务永远进不来。
- 单个
queue/listen进程是串行消费,想提吞吐就得开多个实例(如Supervisor配3个program) - 多个实例共享同一channel时,DB驱动靠行锁防重复取,Redis驱动靠原子操作,不用额外加锁
- 别在Web端启动消费者——
web/config/main.php里加bootstrap会引发Cannot use object of type yii\queue\amqp_interop\Queue as array错误











