必须用think-queue+redis驱动,因thinkphp不内置队列,sleep循环或数据库轮询易僵死、不支持多实例;任务类须定义fire()方法并手动delete(),投递需传类名字符串且监听命令要匹配queue名,延迟队列依赖zset与分布式锁。

队列必须用 think-queue + Redis 驱动
ThinkPHP 本身不内置队列服务,不能靠 sleep 循环、数据库轮询或 while 模拟消费进程。这些方式在生产环境极易僵死、无法平滑重启、不支持多实例伸缩,且无法保证消息不丢、不重。推荐方案是使用社区成熟扩展 topthink/think-queue,搭配 Redis 作为底层驱动——它轻量、高性能、天然支持原子操作与延迟队列(zset),适合中小到中大型项目。
任务类必须定义 fire() 方法并手动 delete()
在 ThinkPHP 6/8 中,队列任务类只识别 fire() 方法,签名固定为 fire(Job $job, $data)。不是 handle(),也不是 run();传参错误或方法名不符,会导致任务被静默忽略,无报错、无日志、无执行。
执行成功后必须显式调用 $job->delete(),否则该任务会持续被取出重试;失败时建议用 $job->fail() 记入 failed_jobs 表,或 $job->release(60) 延迟 60 秒重试。
发布任务要传类名字符串,监听需匹配 queue 名
投递任务时,必须传完整命名空间的字符串,例如:Queue::push('app\job\SendSmsJob', $data, 'sms');
传对象、闭包、带 @ 符号的写法(如 'app\job\SendSmsJob@fire')均无效。
对应地,配置文件 config/queue.php 中设置的 'queue' => 'sms',会生成 Redis key queues:sms。启动监听命令也必须指定相同队列名:php think queue:listen --queue sms
否则消费者无法读取该队列消息。
延迟队列依赖时间戳 + zset,消费端需加分布式锁
订单超时取消、消息定时推送等场景,应使用延迟队列。核心是把过期时间转为 Unix 时间戳,作为 score 写入 Redis 有序集合:$redis->zAdd('order:delay:queue', $expireTs, $orderId);
消费进程需主动扫描 zset 中 score ≤ 当前时间的元素。由于可能部署多实例,必须对每个待处理 ID 加分布式锁(如 SETNX order_lock:123 "1" + EXPIRE),防止重复执行同一笔订单逻辑。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











