分布式调度模型:Master-Worker、Leader-Follower、共享状态与无状态调度

说到分布式调度,很多人第一反应就是「选主」。但说实话,选主只是万里长征第一步。真正让我头疼的,是选完主之后,任务到底怎么分配、状态怎么同步、挂了怎么恢复。

我这些年接触过的调度系统,基本逃不出四种模型:Master-Worker、Leader-Follower、共享状态调度、无状态调度。每种模型都有自己的脾气,选错了,后面全是坑。

Master-Worker架构:最经典,也最容易被误解

Master-Worker,说白了就是一个老板带一群小弟。Master负责拆任务、派任务、收结果;Worker只管埋头干活。

核心逻辑:Master是唯一的大脑,Worker是无状态的执行者。

我个人的习惯:在中小规模集群(几十台节点)里,Master-Worker是最稳的选择。但要注意,Master不能成为瓶颈。

我曾经在一个数据清洗项目里用过这种模型。Master节点负责从消息队列里拉取任务,然后分发给20个Worker。每个Worker处理完就写结果到共享存储。看起来挺完美,对吧?

结果上线第一天就出问题了——Master挂了。所有Worker瞬间变成无头苍蝇,任务全部卡住。

避坑指南:Master-Worker最大的弱点就是单点故障。我曾经吃过这个亏,后来加了备用Master,用ZooKeeper做选主,才算踏实。

适用场景:

  • 任务可以拆分成独立子任务
  • Worker之间不需要通信
  • 集群规模在100节点以内

代码示例(伪代码):

// Master节点
while (true) {
    task = queue.poll()
    worker = selectIdleWorker()
    worker.assign(task)
}

// Worker节点
while (true) {
    task = master.fetchTask()
    result = execute(task)
    master.reportResult(result)
}

Leader-Follower模式:去中心化的优雅方案

Leader-Follower和Master-Worker最大的区别是什么?Leader也是Worker之一。它不光是发号施令的,自己也干活。

你想想看,这种设计天然解决了「Master闲着、Worker累死」的问题。而且Leader挂了,随便拉一个Follower顶上就行,切换成本极低。

核心机制:

  • 所有节点平等,谁抢到领导权谁就是Leader
  • Leader负责协调,但也参与执行
  • Leader挂了,自动选举新Leader

我的经验:在Raft协议里,Leader-Follower模式用得最多。我做过一个日志复制系统,Leader负责接收写请求,然后同步给Follower。Leader自己也要处理读请求,不会闲着。

但这里有个坑——Leader如果既要协调又要干活,压力会很大。我记得有一次压测,Leader节点CPU冲到90%,Follower才30%。后来我调整了策略,让Leader只做轻量级任务,重活丢给Follower。

适用场景:

  • 需要高可用、自动故障转移
  • 节点数量适中(10-50个)
  • 任务执行时间差异不大

共享状态调度:一致性是最大的敌人

共享状态调度,说白了就是所有节点都看同一份数据。谁抢到锁,谁就执行任务。

这种模型的好处是简单——状态都在数据库里,谁都能读。但坏处也很明显:并发冲突、锁竞争、性能瓶颈。

实现方式:

  • 基于数据库的行锁或乐观锁
  • 基于分布式锁(Redis、ZooKeeper)
  • 基于事务日志

我曾经踩过的坑:用MySQL做共享状态调度,任务表里有个status字段。多个Worker同时抢任务,用UPDATE ... WHERE status=0 LIMIT 1。结果并发一高,死锁频发。后来改成Redis的SETNX,才解决问题。

性能对比:

方案 一致性 性能 复杂度
数据库行锁 强 低 低
Redis分布式锁 最终 中 中
ZooKeeper 强 高 高

嗯,这里要注意:共享状态调度不适合高频任务。如果每秒几千个任务,数据库根本扛不住。我建议每秒1000以内的任务量,用Redis锁就够了。

无状态调度:弹性伸缩的终极答案

无状态调度,说白了就是所有节点都不保存状态。任务来了,谁有空谁干,干完就忘。

你想想看,这种模型有多爽——随便加节点、随便减节点,不用考虑数据迁移。Kubernetes的调度器就是典型例子。

核心设计:

  • 任务信息放在外部存储(消息队列、数据库)
  • 调度器只负责分配,不保存任何中间状态
  • 节点挂了,任务重新入队,其他节点接着干

我个人的建议:如果你的业务允许任务重复执行(幂等),无状态调度是最省心的。我做过一个日志处理系统,每天几亿条日志,用Kafka + 无状态Worker,扩容缩容跟玩一样。

但无状态调度也有代价——任务必须幂等。如果任务不能重复执行,你就得额外加去重逻辑,反而更复杂。

适用场景:

  • 任务可以重复执行
  • 需要弹性伸缩
  • 集群规模大(1000+节点)

四种模型对比

模型 状态管理 容错性 扩展性 典型场景
Master-Worker Master集中管理 低(单点故障) 中 批处理、ETL
Leader-Follower Leader协调 高 中 分布式数据库、共识系统
共享状态 外部存储 中 低 任务队列、定时调度
无状态 无 高 高 微服务、流处理

核心逻辑关系图

分布式调度模型核心逻辑 分布式调度 Master-Worker Leader-Follower 共享状态调度 无状态调度 集中管理 单点故障风险 去中心化 自动故障转移 外部存储 锁竞争 无状态 弹性伸缩 选型关键:一致性要求 × 集群规模 × 任务特性

这张图把四种模型的关系理清楚了。你看,从Master-Worker到无状态调度,其实是一条「去中心化」的演进路线。越往右,弹性越好,但实现复杂度也越高。

我的建议:别一上来就追求最复杂的方案。我见过太多团队,明明只有10台机器,非要上无状态调度,结果被幂等和去重搞得焦头烂额。先想清楚你的场景,再选模型。

最后说一句:没有银弹。每种模型都有它的甜蜜点,也有它的痛点。理解了这些,你才能在面对具体问题时,做出不后悔的选择。

可可精品资料库