微服务三大件:调度、任务编排、决策引擎
微服务三大件:调度、任务编排、决策引擎
微服务系统复杂起来以后,很多问题不是服务拆得不够细,而是缺少统一的执行入口、流程控制和规则决策。通常可以把这三类能力归为三大件:调度、任务编排、决策引擎。
一、先定边界:三大件分别管什么
在落地之前,需要会先把边界划清楚,避免所有能力互相侵入。
| 能力 | 解决的问题 | 不建议做的事 |
|---|---|---|
| 调度 | 什么时候触发任务 | 不承载复杂业务流程 |
| 任务编排 | 多步骤任务如何推进 | 不直接写复杂业务规则 |
| 决策引擎 | 根据什么规则选择路径 | 不直接修改核心业务状态 |
一个典型流程可以这样理解:
这里值得关注的是落地时的治理方式,而不是一开始就选一个大而全的平台。
二、调度:团队要控制调度器数量
调度器最容易失控。每个服务都写几个本地定时任务,短期很快,长期会出现重复执行、任务不可见、失败不可追踪、补偿靠人工的问题。
原则是:能少建调度器就少建,能统一就统一。
1. 调度器数量要收敛
我们需要按任务重要性分层:
| 类型 | 建议做法 |
|---|---|
| 本地轻量任务 | 可以留在服务内,例如缓存刷新、临时清理 |
| 核心业务任务 | 进入统一调度平台或统一任务表 |
| 跨服务任务 | 不放在单个业务服务里本地触发 |
| 补偿类任务 | 必须可查询、可重试、可人工触发 |
| 高并发扫描任务 | 必须有分片、限流、并发控制 |
如果团队里已经出现多个调度器,那么一定要做一次盘点:
- 有多少服务在跑定时任务。
- 哪些任务会改业务数据。
- 哪些任务失败后没人知道。
- 哪些任务重复执行会产生脏数据。
- 哪些任务应该迁到统一调度入口。
2. 调度任务必须有执行记录
核心任务不能只依赖日志,要求至少记录这些字段:
| 字段 | 说明 |
|---|---|
| task_id | 任务实例 ID |
| task_type | 任务类型 |
| business_key | 业务唯一键,例如 order_id |
| status | pending/running/success/failed |
| retry_count | 重试次数 |
| next_trigger_time | 下次触发时间 |
| error_message | 最近一次错误 |
| created_at / updated_at | 创建和更新时间 |
有了任务实例,才能做到失败可查、可重试、可补偿。
3. 调度必须做防重和限流
调度系统至少要考虑三件事:
- 防重复触发:多实例部署时要有分布式锁、任务抢占或唯一约束。
- 控制并发:不要一次扫描出大量任务直接打爆下游。
- 失败退避:失败任务不要无限高频重试,可以按 1 分钟、5 分钟、30 分钟退避。
我的经验是,调度器出问题通常不是“不触发”,而是“触发太多、重复触发、失败后没人管”。
三、任务编排:重点是事务和补偿设计
任务编排不是把多个接口串起来这么简单。真正难的是:某一步成功、某一步失败时,系统应该处于什么状态。
例如一个订单履约流程:
- 校验订单。
- 锁定库存。
- 创建发货单。
- 通知仓储。
- 更新订单状态。
- 发送通知。
如果全部写在一个同步方法里,只要中间一步失败,就很难判断该重试、回滚还是人工处理。
1. 先拆事务边界
需要先区分哪些操作必须在本地事务内完成,哪些操作只能做最终一致。
| 操作 | 建议事务方式 |
|---|---|
| 更新本服务状态 | 本地事务 |
| 调用其他微服务 | 不放进本地数据库事务 |
| 发送消息 | 使用事务消息或 outbox 模式 |
| 外部系统调用 | 记录请求状态,失败后补偿 |
| 多服务状态一致 | 用流程状态 + 补偿保证最终一致 |
不要试图用一个大事务包住多个微服务。跨服务流程更适合用 Saga、状态机、任务表、补偿任务来处理。
2. 每个步骤都要可重试、可跳过、可补偿
编排步骤设计时,要给每一步定义四个信息:
| 项目 | 示例 |
|---|---|
| 输入 | order_id、user_id、amount |
| 成功条件 | 库存锁定成功,返回 lock_id |
| 失败处理 | 可重试 3 次,仍失败进入人工处理 |
| 补偿动作 | 释放库存、取消发货单、恢复状态 |
不要只写“调用库存服务”。要写清楚库存服务超时怎么办、重复调用怎么办、锁定成功但后续失败怎么办。
3. 流程状态不能只存在调用栈里
我们需要把流程实例和步骤实例落库,例如:
process_instance
- process_id
- process_type
- business_key
- status
- current_step
- created_at
- updated_at
process_step_instance
- step_id
- process_id
- step_name
- status
- retry_count
- error_message
这样服务重启后,流程还能继续;某一步失败后,也能定位到具体步骤,而不是只能整单重跑。
4. 编排要避免“黑盒流程”
上线后,运维和研发至少要能查到:
- 当前流程执行到哪一步。
- 哪一步失败了。
- 失败原因是什么。
- 已经重试几次。
- 是否可以手动重试。
- 是否需要人工介入。
如果流程不可见,编排系统会变成新的排查黑洞。
四、决策引擎:重点是幂等、版本和可解释
决策引擎不是把 if-else 换成配置表。它真正要解决的是:规则变化快、规则需要审计、规则命中结果需要解释。
常见场景包括:
| 场景 | 决策结果 |
|---|---|
| 风控 | 自动通过、人工审核、拒绝 |
| 审批 | 一级审批、二级审批、跳过审批 |
| 调度分流 | 普通队列、优先队列、人工队列 |
| 营销 | 命中活动 A、命中活动 B、不命中 |
1. 决策引擎要尽量无副作用
原则是:决策引擎只返回结果,不直接改业务数据。
例如它可以返回:
{
"decision_id": "D202606230001",
"result": "MANUAL_REVIEW",
"rule_version": "risk_rule_v12",
"hit_rules": ["amount_gt_10000", "high_risk_user"]
}
业务服务拿到结果后,再决定更新订单状态、创建审核单或进入后续流程。这样可以避免规则系统越来越重,最后变成新的业务大单体。
2. 决策请求必须幂等
决策引擎经常会被重试调用,所以必须考虑幂等。
要求每次请求带上业务唯一键和请求流水号:
| 字段 | 说明 |
|---|---|
| request_id | 本次决策请求唯一 ID |
| business_key | 业务唯一键,例如 order_id |
| scenario | 决策场景,例如 risk_check |
| rule_version | 可选,指定规则版本 |
| input_hash | 输入参数摘要 |
如果同一个 request_id 重复请求,应返回一样的决策结果,最好是同一个结果。否则重试可能导致前后命中不同规则,后续流程就会混乱。
3. 规则要有版本和灰度
规则变更比代码发布更频繁,因此必须有版本管理。
我会要求规则至少具备:
- 草稿、发布、停用状态。
- 版本号。
- 生效时间和失效时间。
- 修改人和审批人。
- 灰度范围。
- 回滚入口。
- 命中日志。
特别是风控、审批、计费类规则,不能只保存最新配置。线上出现争议时,必须能还原“当时到底命中了哪条规则”。
4. 决策结果要可解释
只返回 true/false 不够。至少要返回:
- 命中的规则。
- 使用的规则版本。
- 决策结果。
- 决策耗时。
这样后续排查、审计、客诉处理才有依据。
五、三大件落地顺序
如果团队还没有这三类能力,我不会一上来就做平台化。更稳妥的方式是分阶段推进。
第一阶段:收敛调度
目标:控制调度器数量,让核心任务可见。
具体动作:
- 盘点所有定时任务和延迟任务。
- 标记哪些任务会改业务数据。
- 将核心任务迁入统一调度入口。
- 建立任务实例表。
- 增加失败重试、人工补偿、告警。
第二阶段:沉淀编排
目标:让长流程可追踪、可恢复。
具体动作:
- 梳理跨服务流程。
- 拆分流程步骤和事务边界。
- 建立流程实例和步骤实例。
- 为关键步骤补充重试和补偿动作。
- 提供流程查询和手动恢复入口。
第三阶段:治理规则
目标:把频繁变化的判断从代码中抽出来。
具体动作:
- 先找变化频繁的规则,例如风控、审批、分流。
- 建立规则配置和版本管理。
- 增加规则命中日志。
- 设计幂等请求和结果缓存。
- 支持灰度发布和快速回滚。
第四阶段:统一运维入口
目标:降低排查和运营成本。
具体动作:
- 一个页面能查任务实例、流程实例、决策记录。
- 支持失败任务重试。
- 支持流程步骤恢复。
- 支持规则版本回滚。
- 接入监控告警和权限审计。
六、上线前检查清单
调度器检查
任务编排检查
决策引擎检查
总结
微服务里的调度、任务编排、决策引擎,落地时不要只停留在概念上,需要继续关注三件事:调度器数量要收敛,任务编排要把事务和补偿设计清楚,决策引擎要保证幂等、版本和可解释。
这三类能力做好以后,系统的执行过程才会可见,失败后才有恢复路径,规则变化也不会频繁冲击业务代码。真正成熟的微服务系统,不是服务拆得多,而是任务能管住、流程能恢复、决策能解释。

浙公网安备 33010602011771号