thinkphp 8 中需手动安装 think-queue 扩展并注册服务提供者,配置迁移、区分 dispatch() 与 dispatchnow() 使用场景,注意事务安全、worker 异常捕获、maxtries/delay 设置及 failed() 方法正确实现。

ThinkPHP 8 的 think-queue 不再默认集成,得手动装
TP8 把队列抽成独立扩展,直接用 queue:work 会报错 Command "queue:work" is not defined。这不是你配置错了,是根本没装包。
- 运行
composer require topthink/think-queue(注意不是topthink/think-queue的旧版别名) - 装完要手动注册服务提供者:在
app/provider.php里加一行think\queue\ServiceProvider::class - 别漏掉配置文件:执行
php think queue:table生成迁移,再php think migrate:run—— 否则failed_jobs表不存在,失败任务直接丢弃
dispatch() 和 dispatchNow() 别混用场景
前者把任务推到中间件(Redis/Database),后者直接同步执行——看着像“立即”,实则是绕过队列、在当前请求里跑完。压测时发现耗时突增,大概率是误用了 dispatchNow()。
- 发邮件、写日志、调第三方 API 这类耗时操作,必须用
dispatch() -
dispatchNow()只适合单元测试或调试:比如想验证 Job 类逻辑是否正确,又不想起 worker 进程 - 数据库驱动下,
dispatch()默认不事务安全;如果主流程回滚了,任务仍会执行——要用afterCommit()包一层
Worker 进程卡住不消费?先看 maxTries 和 delay
常见现象:任务进表了,但 queue:work 没日志、CPU 占用低、队列表里 attempts 一直为 0。大概率是 job 构造函数抛异常,连反序列化都没过,worker 直接跳过该任务。
- 检查 job 类的
__construct():不能有外部依赖未绑定、不能调用未初始化的容器实例 -
maxTries设太小(比如 1),失败后进failed_jobs就停了;设太大又可能掩盖逻辑缺陷 -
delay值单位是秒,不是毫秒;设delay(1000)是等 1000 秒,不是 1 秒 - Redis 驱动下,记得配
redis.default.queue,否则多个应用共用默认队列名,互相干扰
失败任务重试时,$this->fail() 和抛异常效果不同
在 job 的 handle() 里,主动调 $this->fail() 会进 failed_jobs 表并触发 failed() 方法;而抛出未捕获异常,取决于 maxTries 是否用尽——容易漏掉清理逻辑。
- 需要自定义失败处理(比如通知运维、回滚外部状态),必须实现
failed()方法,并在其中调$this->fail() - 别在
failed()里再抛异常,会导致重复记录失败日志且无法被queue:retry捕获 - 数据库驱动下,
failed_jobs.exception字段只存前 65535 字节,超长堆栈会被截断——查问题时得结合日志系统看完整异常
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











