Skip to content

分布式任务调度分片

起因是这样的,我之前所在的公司(2023年之前)都是单实例的Job,所以不存在分布式场景下的问题需要处理。这也意味着如果有大量的数据需要处理,始终只有一个节点在哼哧哼哧的处理,效率非常低。

而我去面试的时候,不止一次被问到分布式场景下Job是怎么处理的。包括我现在所在的公司,他们在job的处理上也进行了分布式场景下的分片操作。

那么如何进行分片呢?

现在很有多分布式任务调度框架支持分片,例如xxl-job,支持在代码中获取当前分片和分片总数,在程序里对固定区间的数据根据总分片数做分片计算即可。

假设现在有3个Job实例,数据量为10:

现在基于支持分片功能的分布式调度中心框架来实现分片

分片总数(totalSharding)一般为有效的实例总数,这里为3, 即 totalSharding = 3

需要处理的数据量(total)为10,即 total = 10

每个分片需要处理的数据量为 total / totalSharding, 如果有余数,交给最后的一个Job节点处理(这里只是一个简单的策略,实际生产环境可能会根据谁处理更快、谁资源充足等各种条件来决策)

即:

  • JobA处理的数据量为: 1-3
  • JobA处理的数据量为: 4-6
  • JobA处理的数据量为: 7-10

如果分布式调度框架不支持分片,每次都是所有的节点一起触发,又要怎么处理呢?

其实很简单,我们模拟支持分片的分布式调度框架就好了。支持分片的分布式调度框架之所以支持分片,是因为框架在内存(或者其他地方)维护了实例数和用户期望的分片数据。基于这个思想我们就可以搞一个地方来存储,例如放到Redis、配置中心、DB、(甚至可以是)文件等等

在这里举一个例子,有10个店铺需要拉单,期望每个Job实例处理一部分店铺。我们只要把每个店铺所负责的店铺维护在Redis就可以了。在任务触发时根据实例所负责的店铺数据来拉单就好了,如果不是自己所负责的,则不用管。

即:

  1. 每个Job实例都有一个唯一标识,例如 Job实例的 ip + 端口,如果还不能判断唯一,就继续添加标识,例如机器码,直到可以判断唯一即可。实在不行,就采用抢占式的方案来竞争,这样就不用费劲找唯一标识了
  2. 将10个店铺按照实例数简单的切分一下(如果公司有切分规则,按照规则切分就好),放到一个数据结构里就好,例如放到Map<String,List<String>> instanceShopIdMap, key是实例的唯一标识,value是实例所负责的店铺。将instanceShopIdMap转化为Json存储到Redis即可
  3. 在每个job执行任务时,通过instanceShopIdMap.get(实例的唯一标识)来获取所负责的店铺,执行后续的业务逻辑即可

instanceShopIdMap接口最好写一个定时任务,定时刷新,因为可能会存在新增、删除店铺之类的操作,或者Job实例扩容、缩容等操作

最后

现在基本上都是集群了,很少有单机。当然现在有很多框架帮我们做了分配,这篇文章的内容旨在告诉大家如何自己实现任务分配,对于平时看代码或者业务实现可能会有一定的帮助

Last updated: