12.3 两层调度 (Mesos)

一句话

Mesos 把调度拆成两层:分配器(allocator) 决定 “把哪些资源给谁”,框架调度器(framework scheduler) 决定 “用这些资源跑哪些任务”;它用 offer(资源报价) 代替共享状态,避免了全局锁,但 offer 会 过期——资源给你了,你犹豫,就浪费了。

两层分工

层 角色 职责 不做什么
资源层 Master + Allocator 汇总 Agent 资源,按策略(DRF/公平)生成 offer 不决定具体任务放哪
框架层 Framework Scheduler(如 Spark/Marathon) 收到 offer,决定接受/拒绝、跑哪些任务 不直接访问 Agent 资源
Agent 层 每台机器上的 agent 上报资源、启动 executor 不参与全局分配

offer 生命周期:生成 → 发给框架 → 接受(资源锁定)→ 启动任务 → 拒绝/超时(资源回收再分配)。

实测:offer 过期导致的拒绝

模拟 300 节点 × 8 核、2000 个 1 核任务,15% 的决策基于过期 offer:

架构 成功 拒绝/冲突
单体 2000/2000 0
两层(offer) 1860/2000 7.0% 拒绝
共享状态 K=4 1878/2000 23.1% 冲突

7% 的拒绝不是 “bug”,而是 offer 模型的固有成本:框架拿到 offer 时资源还在,决定用的时候可能已被别人拿走或已过期。这也是 Mesos 后来引入 “资源预留/revocable offers” 的原因。

浪费率 ≈ 拒绝率 × offer 平均持有时间 / 决策周期
优化方向:缩短 offer 超时 + 让框架 “快速决策”

代码:offer 的接受与拒绝

func (f *Framework) OnOffer(o Offer) {
    need := f.pendingTasks()
    if len(need) == 0 {
        f.decline(o)                 // 没任务就别占着,立即归还
        return
    }
    fit := filter(o.Resources, need) // 框架自己决定放哪些
    if len(fit) == 0 {
        f.decline(o)
        return
    }
    f.accept(o, fit)                 // 接受后资源锁定,启动 executor
}

关键:框架必须 “要么用、要么立刻还”,犹豫就会让资源闲置,而分配器无法感知 “框架在思考”。

反模式

反模式 后果
框架收到 offer 后慢慢决策 offer 过期,资源浪费,集群利用率下降
一个框架垄断所有 offer 多框架公平性失效(需要 DRF/配额)
offer 粒度太大(整机) 小任务用不完,资源碎片化
offer 粒度太小(单核) 通信与决策开销爆炸
分配器不做 “拒绝回填” 被拒资源闲置到下一个周期
框架无 “资源需求声明” 分配器只能猜,offer 命中率低

三条铁律

🔑 offer 是 “限时报价”:不立刻用就还回去。

🔑 两层调度的收益是 “解耦与多框架”,代价是 “信息陈旧”。

🔑 offer 粒度 = 资源利用率与开销的平衡点,不是越细越好。

自检

Q1 两层调度相比单体,最大的收益是什么?

▸ 答案:解耦与多框架共存:资源分配(全局策略)与任务放置(框架自己的语义)分开,不同框架(Spark、Marathon、Hadoop)可以共享一个集群,而不需要把各自的调度逻辑塞进一个中心调度器。代价是 offer 的信息陈旧与拒绝浪费。

Q2 为什么 offer 会 “过期”?

▸ 答案:因为资源是动态的:分配器发 offer 时资源空闲,但框架决策需要时间,期间资源可能被其他框架拿走、或节点故障、或 offer 超时。Mesos 用 “超时回收” 处理,框架则要 “快决策、早归还”。

Q3 DRF(主导资源公平)在两层调度里起什么作用?

▸ 答案:它决定 offer 给谁:按 “各框架的主导资源份额” 排序,份额小的优先。这样多框架之间的资源分配接近公平,而不是 “谁先请求谁拿”。DRF 属于分配器层,框架层不感知(见 12.5 公平调度算法 (MMF / DRF))。

Q4 offer 粒度为什么不能太细?

▸ 答案:因为每次 offer 都有通信与决策开销:生成、传输、框架评估、接受/拒绝、状态更新。粒度到 “单核” 时,一个 800 核集群每轮要发几百个 offer,框架侧的处理成本会超过调度收益。

Q5 框架 “拒绝 offer” 有什么代价?

▸ 答案:短期是资源闲置(从拒绝到下一轮分配之间),长期是分配器对该框架的需求估计变差(它不知道你是 “不需要” 还是 “暂时不需要”)。所以框架应尽量 “接受能用的部分”,而不是整单拒绝。

Q6 两层调度适合什么场景,不适合什么?

▸ 答案:适合多框架共享集群、框架有自己复杂调度语义的场景(大数据生态)。不适合 “单一业务、追求极致利用率” 的场景——此时单体或共享状态更简单高效。判断依据是 “是否需要多种调度语义共存”。

Q7 现代系统还用两层调度吗?

▸ 答案:思路还在,形态变了:K8s 用 “调度器 + 扩展点(scheduler framework)+ 多调度器”,YARN 用 “ResourceManager + ApplicationMaster”,都是 “资源分配与任务放置分离” 的变体。纯粹的 “offer 模型” 在大数据之外较少见,因为它的信息陈旧成本较高。

关联