thinkphp 6 本身不提供分布式定时任务调度能力,需通过中心化触发(单台调度机配置 crontab)、分布式队列执行(多节点监听同一 redis 队列)、统一状态存储(redis 锁+数据库条件更新)及容错监控(重试、日志分片、prometheus 指标)协同实现。

ThinkPHP 6 本身不提供分布式定时任务调度能力,所谓“分布式部署”,本质是多台服务器协同执行同一套定时逻辑,同时避免重复触发、状态冲突和单点失效。这不是框架内置功能,而是靠架构设计+外部组件+严谨约定来实现。核心思路是:用中心化触发 + 分布式幂等执行 + 状态隔离。
一、触发层必须集中,不能多机各自跑 crontab
每台应用服务器都配相同的 crontab 规则,会导致任务被多次执行(比如每台机器每分钟都调用一次订单同步)。正确做法是:只在一台专用调度机(或主控节点)上配置 crontab,其他机器不挂任何定时规则。
- 调度机可以是独立的轻量级 CVM,也可以是主 Web 服务器中指定的一台
- crontab 命令仍需绝对路径、cd 到项目根目录、重定向日志,例如:
*/5 * * * * cd /var/www/myapp && /usr/bin/php think sync:order >> /var/log/sync-order-cron.log 2>&1 - 该命令不直接处理业务,而是发布一个队列任务(如 Queue::push('app\job\SyncOrderJob', $data)),把执行权交给队列系统
二、执行层靠队列实现天然分布式
所有应用服务器都运行相同的队列消费者进程(php think queue:listen --queue order),它们从同一个 Redis 队列(如 queues:order)取任务。Redis 的原子性 pop 操作天然保证:一个任务只会被一台机器消费。
- 确保所有节点连接的是同一个 Redis 实例(或高可用集群),且
config/queue.php中的'redis' => [...]配置完全一致 - 每个节点启动消费者时,必须显式指定队列名:
php think queue:listen --queue order,不能省略--queue - 建议开启
--daemon模式并配合 supervisor 管理进程,防止意外退出
三、关键状态必须统一存储与校验
分布式环境下,“谁在执行”“执行到哪了”“是否已完成”这些状态不能存在本地文件或内存里,必须落库或存 Redis。
- 例如订单超时取消任务:先用
$redis->zAdd('order:delay:queue', $expireTs, $orderId)写入延迟队列;消费时用setNx('lock:cancel:'.$orderId, '1')加锁,成功才执行取消逻辑,完成后删锁、删订单记录 - 数据库操作务必加唯一索引或乐观锁,比如对订单状态字段做条件更新:
Db::table('order')->where(['id'=>$id, 'status'=>'wait_pay'])->update(['status'=>'closed']) - 避免使用
runtime/cache/存状态——它只在本机有效,跨机器不同步
四、容错与可观测性不能少
分布式意味着故障点变多,必须提前设计降级与追踪能力。
- 队列任务失败后,用
$job->release(120)延迟 2 分钟重试,最多尝试 3 次后调用$job->fail()进入失败队列 - 所有队列任务的日志写入统一通道(如 Log::channel('queue')->info()),日志文件按节点命名(
queue-node1.log),方便定位问题机器 - 用 Redis 的
INCR统计每日任务总量、失败数,再通过 Prometheus + Grafana 可视化监控
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











