1. 分布式任务调度系统概述
在当今互联网应用中,定时任务调度是一个常见需求。传统的单机crontab方案存在诸多局限性:无法水平扩展、缺乏任务状态跟踪、没有失败重试机制等。本文将介绍如何使用PHP构建一个高可用的分布式任务调度系统,其核心思想是将"任务触发"和"任务执行"分离,通过Redis作为中间件实现解耦。
这个方案的主要优势在于:
- 调度中心(Scheduler)和执行器(Worker)可以独立部署和扩展
- 任务执行状态全程可追踪
- 内置失败重试机制
- 支持多种任务路由策略
- 天然具备高可用特性
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 整体架构
系统采用典型的生产者-消费者模式:
code复制[调度中心 Scheduler] → Redis → [执行器节点 Worker x N]
↑ ↓
定时触发 执行任务+上报结果
调度中心负责:
- 定时检查哪些任务需要执行
- 将任务分发到合适的Worker队列
执行器节点负责:
- 从自己的队列获取任务
- 执行具体业务逻辑
- 上报执行结果
Redis作为消息中间件,存储:
- 任务队列
- Worker在线状态
- 任务执行日志
- 分布式锁
2.2 核心组件选型
-
PHP框架选择:
- 使用Swoole扩展提供高性能定时器和进程管理能力
- 选择Predis作为Redis客户端库
-
Redis数据结构设计:
- List:存储每个Worker的任务队列(queue:worker:[id])
- Set:存储在线Worker列表(workers:online)
- Hash:存储任务执行日志(job:log:[taskId])
- String:实现分布式锁(scheduler:lock)
-
部署方案:
- 调度中心:可部署1-3个实例(通过分布式锁保证单实例工作)
- 执行器:可水平扩展多个实例
- Redis:建议使用集群版保证高可用
3. 调度中心实现细节
3.1 任务定义与注册
php复制$jobs = [
'order.timeout' => [
'cron' => '*/5 * * * *', // 每5分钟执行一次
'route' => 'round_robin' // 轮询方式分发
],
'report.daily' => [
'cron' => '0 2 * * *', // 每天凌晨2点执行
'route' => 'random' // 随机分发
],
'cache.warmup' => [
'cron' => '*/1 * * * *', // 每分钟执行一次
'route' => 'broadcast' // 广播给所有Worker
]
];
关键设计考虑:
- cron表达式支持标准格式,兼容现有crontab配置
- 路由策略根据任务特性选择:
- broadcast:适用于需要所有节点执行的任务(如缓存预热)
- round_robin:适用于需要负载均衡的任务
- random:适用于简单任务分发
3.2 分布式锁实现
php复制function acquireLock(Client $redis, string $key, int $ttl = 30): bool {
return (bool) $redis->set($key, gethostname(), 'EX', $ttl, 'NX');
}
实现原理:
- 使用Redis的SET NX命令实现原子性获取锁
- 设置TTL防止死锁(即使持有锁的进程崩溃,锁也会自动释放)
- 锁值为当前主机名,便于排查问题
最佳实践:
- 锁的TTL应大于任务检查周期(本例为60秒检查一次,TTL设为30秒)
- 获取锁失败后应立即返回,避免不必要的资源竞争
- 生产环境建议实现锁续期机制
3.3 定时任务触发
php复制Timer::tick(60000, function() use ($redis, $jobs) {
if (!acq
