thinkphp本身无统一队列接口,依赖think-queue扩展实现;入队仅用queue::push()(立即)或queue::later()(延迟),任务类以fire()为入口,需调用$job->delete()移除任务。

ThinkPHP 本身不提供“任务队列接口”这个抽象概念,也没有统一的 queueInterface 或 QueueContract 供你直接实现——它靠的是 think-queue 扩展包 + 具体驱动(如 redis、database)来落地队列行为。所谓“实现接口”,实际是配置驱动、写任务类、调用推送方法这三步闭环。
怎么把任务塞进队列:用 Queue::push() 还是 Queue::later()
这两个是唯二真正对外暴露的“入队接口”,不是你自己定义的:
-
Queue::push($job, $data, $queue):立即入队,worker 启动后马上拉取执行 -
Queue::later($delay, $job, $data, $queue):延迟入队,$delay 单位是秒,比如300表示 5 分钟后执行 - $job 是字符串,不是对象:单模块下写
'Job1'(对应app\job\Job1),多模块写'admin/Job2',带方法名则写'app\lib\job\Job2@task1' - $data 必须可序列化,别传
$this、闭包、资源句柄;建议只传数组或简单对象(如['user_id' => 123, 'template' => 'welcome']) - 如果
$queue不填,默认走配置里的default队列名,但 worker 监听时必须显式指定:php think queue:listen --queue=default
任务类里没有 handle() 方法?那是旧文档误导
ThinkPHP 6 + think-queue 2.x/3.x 的任务类不需要继承任何基类,也不强制叫 handle()。官方示例和实际运行都以 fire() 为默认入口:
- 如果你只写一个方法,就叫
fire(Job $job, $data),框架会自动调用它 - 如果你想拆多个逻辑,比如发短信、发邮件、写日志,可以分别写
sendSms()、sendEmail(),发布时用'Job2@sendSms' - 别漏掉
$job->delete(),否则任务不会从队列移除,会不断重试直到超限触发failed() -
$job->attempts()返回当前重试次数,可用于做指数退避,比如第 1 次失败后 1 秒重试,第 2 次 2 秒,第 3 次 4 秒
为什么 php think queue:listen 没反应?检查这三点
这是最常卡住的地方,不是代码问题,而是环境或配置没对齐:
- 确认
config/queue.php里'connector' => 'redis'(或'database'),且对应连接参数(如host、port、password)能连通;Redis 密码为空时别留空字符串,设为null - 确认 Redis 服务正在运行,且数据库索引(
select)没被其他应用占用;database 驱动要确保jobs表存在,字段结构符合think-queue要求 - 别在开发环境用
sync驱动还指望异步效果——'connector' => 'sync'就是同步执行,push()调用后立刻跑完,根本不会进队列 - worker 进程必须持续运行,不能用
php think queue:work(一次性消费)代替listen(长连接监听),除非你配合 supervisor 管理进程
真正容易被忽略的点是:任务类命名空间和自动加载路径必须被 Composer 的 autoload 规则覆盖,否则 worker 找不到类,报 Class "app\job\Job1" not found;别只测试 push() 成功就以为通了,一定要验证 fire() 真的被执行、$job->delete() 真的删掉了 Redis 里的 job 数据。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











