调度平台核心架构:任务生命周期、状态流转模型、调度器与执行器交互流程
调度平台的核心在于将一次业务请求转化为可追踪、可恢复、可并行的任务执行单元。理解任务从创建到终结的完整生命周期、状态之间的合法转换路径,以及调度器与执行器之间的通信协议,是后续利用 Redis 缓存优化索引命中率的前提。
2.1 任务生命周期与状态定义
一个标准的调度任务通常经历以下五个阶段:创建 → 等待 → 调度 → 执行 → 终结。每个阶段对应一组明确的状态值,平台通过状态机保证状态流转的原子性与一致性。
| 阶段 | 状态值 | 说明 | 触发动作 |
|---|---|---|---|
| 创建 | INIT | 任务实例已持久化,尚未进入调度队列 | API 请求 / 定时触发 |
| 等待 | QUEUED | 任务已入调度队列,等待分配执行器 | 调度器轮询或事件推送 |
| 调度 | DISPATCHED | 调度器已选定执行器,发送执行指令 | 调度器下发 RPC 或 MQ 消息 |
| 执行 | RUNNING | 执行器正在处理任务逻辑 | 执行器接收指令并启动线程 |
| 终结 | SUCCESS / FAILED / TIMEOUT | 任务执行完毕,结果已回写 | 执行器上报结果 / 超时检测 |
设计要点:状态值必须使用枚举或整型常量,禁止使用字符串硬编码。推荐在数据库与缓存中同时存储状态,缓存用于快速过滤,数据库用于最终一致性保证。
2.2 状态流转模型
状态流转必须遵循有向无环图(DAG)规则,不允许非法跳转。以下为调度平台的标准状态机模型:
状态机校验规则:
- 只有 QUEUED 状态的任务可以被调度器取出并置为 DISPATCHED。
- 执行器收到指令后,必须将任务状态从 DISPATCHED 更新为 RUNNING,否则调度器会触发重试或超时回收。
- 终结状态(SUCCESS / FAILED / TIMEOUT)为终态,不可再流转回任何中间状态。
- 超时检测由调度器侧的心跳监控线程触发,执行器侧不主动产生 TIMEOUT 状态。
2.3 调度器与执行器交互流程
调度器(Scheduler)与执行器(Executor)是物理分离的组件,通过中间件(Redis / MQ / RPC)进行异步通信。一次完整的任务调度交互包含以下 6 个步骤:
- 任务提交:上游系统通过 API 将任务写入数据库,状态为 INIT,同时将任务 ID 推入调度队列(Redis List 或 Kafka Topic)。
- 调度器拉取:调度器轮询或监听队列,获取待调度任务 ID,查询数据库获取完整任务元数据。
- 执行器选择:调度器根据路由策略(轮询、一致性哈希、最小负载)选定目标执行器,将任务状态更新为 DISPATCHED。
- 指令下发:调度器将执行指令(任务 ID、参数、超时时间)写入执行器对应的待处理队列(Redis Stream 或 MQ Queue)。
- 执行器消费:执行器从队列中获取指令,更新任务状态为 RUNNING,启动工作线程执行任务逻辑。
- 结果回传:执行器完成任务后,将结果(SUCCESS / FAILED + 输出数据)写入回调队列,调度器消费后更新数据库状态。
2.4 关键设计原则
| 原则 | 说明 | 对索引命中率的影响 |
|---|---|---|
| 状态驱动 | 所有调度行为由状态变化触发,而非定时扫描全表 | 减少无效查询,缓存只需索引活跃状态的任务 |
| 异步解耦 | 调度器与执行器通过队列通信,不共享内存 | 缓存层可独立优化,不受执行器负载波动影响 |
| 幂等回传 | 执行器结果回传支持重复消费,调度器侧做去重 | 缓存状态更新可批量合并,减少写放大 |
| 超时兜底 | 调度器维护独立超时检测线程,不依赖执行器主动上报 | 缓存中过期状态可被及时清理,避免脏数据占用索引 |
常见误区:很多团队在设计初期将调度器与执行器合并部署,或使用数据库轮询代替队列驱动。这种做法在任务量超过 1000 TPS 时会导致数据库连接池耗尽、索引频繁锁竞争,最终使 Redis 缓存层也因回源查询过多而命中率骤降。务必从架构层面将调度器与执行器物理分离。