thinkphp异步任务必须用think-queue配合redis驱动实现请求与耗时逻辑解耦;需显式配置retry_after、failed_jobs,禁用--daemon而改用supervisor守护进程,延迟任务须防ntp回拨导致zset跳过执行。

想让用户注册后立刻看到成功页面,而不是干等3秒发完邮件才响应;想上传Excel后马上跳转,而不是卡在“正在处理中”——这些场景必须靠队列把耗时逻辑彻底剥离出HTTP请求生命周期,否则高并发下Nginx超时、PHP-FPM进程池耗尽、用户体验崩盘全是分分钟的事。
安装与驱动配置:别用sync,也别信默认值
执行composer require topthink/think-queue安装扩展包,这一步不能跳过,否则后续所有操作都无效。
在config/queue.php中显式覆盖默认配置,尤其注意'default' => 'redis'——【sync驱动只适合本地调试,线上启用等于没异步】。
Redis连接参数必须完整填写:'host'、'port'、'password'、'select'一个都不能留空或沿用.env里未定义的占位符,否则消费者启动时直接报错退出,任务全积压在内存里不落地。
任务类写法:fire方法里删掉sleep,加一句$job->delete()
新建app/job/SendEmail.php,继承\think\queue\Job,在fire方法开头就校验$data['to']格式,非法邮箱立即$job->fail()退出。
邮件发送逻辑完成后,必须手动调用$job->delete()。ThinkPHP不会自动清理已执行成功的任务,不删就会让Redis的List长度无限增长,最终触发ERR wrong number of arguments错误。
绝对不要在fire里写sleep(2)模拟延迟——这会锁死整个worker进程,同一时刻只能处理1个任务,吞吐量归零。
投递任务:用Queue::later()前先确认系统时间没被NTP回拨
方法一:即时执行用\think\Queue::push('app\job\SendEmail', $data, 'email'),适用于普通通知类任务。
方法二:延迟执行必须用\think\Queue::later(600, 'app\job\SendEmail', $data, 'email'),但要注意——【若服务器启用了NTP自动校时且发生时间回拨,ZSET中已到点的任务会被永久跳过】。
方法三:对顺序敏感的延迟任务(如订单超时关单),改用时间戳+自增序号拼接score:$score = time() . '.' . $seq,再通过$this->redis->zadd()直写,绕过think-queue默认的纯时间戳方案。
启动消费者:禁用--daemon,改用supervisor守护
第一步:创建supervisor配置文件/etc/supervisor/conf.d/tp6-queue.conf,内容包含command=php /var/www/tp6/think queue:work --queue=email,default --sleep=3 --max-jobs=1000 --memory=128。
第二步:运行supervisorctl reread && supervisorctl update && supervisorctl start tp6-queue:*,确保进程状态为RUNNING。
第三步:检查日志tail -f /var/log/supervisor/tp6-queue-*.log,看到[2026-06-01 15:09:00] Processing: app\job\SendEmail即表示消费正常。
切记不要在终端前台执行php think queue:work --daemon——PHP-FPM环境下该参数会导致内存持续泄漏,跑满24小时后worker进程RSS内存突破512MB,自动被Linux OOM killer杀死。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











