分布式调度系统(3)——任务分配

一、问题定义

将任务分配到具体执行节点。

问题输入

  1. 在控制台界面配置的任务的load:
    (1)load可以理解为任务执行占用的资源,或对执行节点造成的负载。
    (2)任务的load是相对的,在同一个集群中应统一标准, 比如估算任务执行的毫秒数作为设置的数值。
    (3)在控制台配置界面,可以分别设置父任务和子任务的load,子任务的load为执行一个子任务的负载。
    (4)在控制台配置界面,对于分片执行的任务,需设置shardingCount;对于动态提交子任务的,需要估算子任务的数量并配置chilrenCount。

以上配置信息保存在/config/job/{jobName}节点。

  1. 在线节点信息保存在/runtime/node/{nodeName}
    /runtime/node/{nodeName}/job节点上保存了配置到当前执行节点的总load值;
    /runtime/node/{nodeName}/jobInstance/runtime/node/{nodeName}/childJobInstance两个节点上保存当前节点实时load值(每10分钟统计,移动平均)。

问题输出

  1. 当前节点执行哪些job,保存在:/runtime/node/{nodeName}/job/{jobName},包含具体的分片编号和子任务顺序号。
  2. 当前任务使用哪些节点,保存在:/runtime/job/{jobName}/avaliableNodes/{nodeName},包含具体的分片编号和子任务顺序号。

二、算法描述

任务分配的目标是:让当前工作尽可能均衡地在各执行节点上执行。
触发任务分配的事件,可能是:

  • 任务启动(可能是新任务启动,可能是任务暂停后重新启动)。
  • 节点上线(需要分配任务到新节点,任务配置preferNode中包含该节点)。
  • 节点下线(原节点执行的任务需要重新分配)。

注意:任务执行超时,"临时"调度到其他节点执行,不属于任务重新分配范畴。

1. 构建待分配任务列表

任务分配单元: 将任务拆分为最小可执行的粒度,如:一个父任务、一个shard子任务,一个子任务,作为一个分配单元。每个分配单元包括:(父任务名称,子任务shardItem序号/childIndex流水号,load)。
待分配任务列表:按任务分类,每个任务形成一个待分配列表,按load倒叙、sharingItem/childIndex正序 排序。
节点下线:在节点下线的场景,需要将故障节点下的任务摘下,按任务放入多个待分配任务列表。
任务启动:对于任务启动的场景,任务下所有的任务分配单元均需要重新分配,都放入到待分配任务列表中。
节点上线:在节点上线的场景中,需读取/config/node/{nodeName}数据, 确定受影响的任务。读取/runtime/job/{jobName}/availableNodes/{nodeName}确认受影响的节点。 计算新节点上线后平均每节点load,从负载最多的节点摘下。摘取时,每次摘取不同任务,直至节点node在平均值以下。

2. 计算各节点load

构建节点列表,构建Map

3. 分配任务

逐个任务分配,优先分配总load高的任务。
每个任务按load倒叙,每次分配分配到不同节点,优先分配总load低的node。
如果节点load已经高出平均值,则退出待分配node列表。

4. 集群启动和关闭

集群启动过程中, 节点不断加进来, 不断触发任务重新分配, 这是没有必要的。
冷冻时间:集群初始启动过程中,第1个启动的节点确认没有其他节点后,会创建/runtime/frozonTime节点,节点数据为解冻的时间点(一般为5分钟后)。
在解冻时间到达之前,不触发任务分配。
创建冷冻节点的同时,清理availableNodes,和/runtime/node/{nodeName}/job

集群收到 shutdown 调用后,开始关闭集群,关闭顺序:

  1. 等待正在执行的任务执行完成 或 超时(超时的任务需达到重试次数)。
  2. 完成任务归档。
  3. 清理/runtime节点。
  4. 节点停止运行。

三、实现

你可能感兴趣的:(分布式调度系统(3)——任务分配)