Tbschedule源码阅读6:对分布式调度系统的思考

虽然把Tbschedule的源码读了一遍,但是如果说让自己来设计一个分布式的调度系统,顿时感觉源码又白读了,我还是不会。那从里面学习到了什么呢?细想一下,能够找出来的东西屈指可数,CopyOnWriteArrayList的用法?Synchroniezed关键字用法又回忆了一次?很显然,这不是我想要的结果。如果仅仅是学习这些简单的东西,完全没必要看这么多复杂的代码。因此,静心下来,回想下自己所学到的东西是什么。

什么是分布式调度任务

在自己工作开始就用到了调度任务,比如凌晨数据状态的变更,定时mq消息的处理(秒级别),甚至是有时候数据的清洗、初始化等都用到了分布式调度任务。为什么要用到调度任务呢?想想这些用到调度任务的例子,其都具有一个共性:在特定的时候处理大量的异步业务数据。 其实仅仅对于调度任务而言,Java提供的Timer就可以满足很多需求,为什么又需要一个分布式调度任务呢?我想其中最重要的原因应该有:

  • 调度的易控性:
  • 调度任务的多样性;
  • 调度任务日趋复杂;
  • 数据量的日益增加;

很明显,对于第一点,如果用了Timer, 那么每一次更改都需要重新上线,增加了操作的复杂性;第二,三点由于我们的业务的多样化,可能需要调度任务并发,支持多种模式; 第4点可能需要更多的机器去处理某些调度任务,可以动态的扩容。
通过查阅网上多款分布式调度任务,发现其有一些重要的共性:

  • 简单
  • 动态,持动态修改任务状态、暂停/恢复任务,以及终止运行中任务,即时生效
  • 弹性扩容缩容 ;
  • 一致性

细想一下,所谓分布式调度,满足这些也就是一些最基本的内容。

如何设计一个分布式调度任务

抛开任务执行的一致性和准确性,因为这是最基本的要求,无论调度 任务是什么样子的,这都是必须满足的。作为一个分布式调度任务,最重要的是要支持动态的扩容缩容。那么如何做到这一点呢?

很容易想到,要做到此点,必须要能够知道近乎实时的调度情况。最简单的情况下,知道任务的数量(任务的情况),能执行某个任务的调度机的数量(调度机的情况)。ok,这样我们就可以对任务进行分配。最简单的策略,平均分配。若减少一个机器,那么剩下的机器来平均分配,反之亦然。

那么问题来了,我们要如何实时的知道任务的数量和执行某个任务的调度机的信息呢? 很显然,需要一个信息共享的东西,我们每次从这个地方读取这些信息。比如增加一个任务,ok, 把增加的这个任务写到共享中心,那么其他的调度机定时的拿到这个信息,知道了有新的任务增加;同样的如果增加和减少了机器,那么将机器注册或注销,下一个时刻,其他的机器知道了新的机器的情况。

这样需要考虑的问题是:用什么样的东西来作为这个共享中心呢? 首先想到的就是zookeeper 和redis。为了让我有信心完成自己的思考,我选择用zookeeper, 毕竟刚刚看过Tbschedule的源码。

首先调度系统可能被很多的业务系统使用,如会员,营销等,那么我们需要将这些业务系统分离开来,因此设计“调度域”的思想,每一个业务系统都有着自己独立的调度空间,那么很自然的就有了TBschedule中rootPath的说法。需要在ZK上建立调度域这样的节点。

其次根据前面说的,我们需要知道调度机的情况,因此当有新的机器加入调度行业的时候,需要将其写入到zk对应的节点,这样就有了Tbschedule中factory的节点;

然后就任务的信息,自然而然的就有了TBschedule中policy和baskTaskType,对于这一点,其实完全可以将其合并成一个。(当然,分开的话可以一个任务对应多个策略。)
想象一下,某个策略可能被很多机器调度,那么这些机器怎么知道该调度哪一个任务呢?这就需要将每个任务应该被哪个机器执行的信息实时更新,方便后面每个机器去查看是否该执行这个任务以及获取执行任务的一些信息。
对于TBschedule而言,增加了管理线程组这样中间一层,即机器先平均分配配置的管理线程组,然后在管理线程组下再开启执行线程,这样进一步增加了对机器性能的压榨。

想想整个流程,其实最主要的是我们想要做什么?知道了想要做什么,有了自己的思考再结合别人的想法,这样才能受益匪浅或者影响更加的深刻。

到此,整个Tbschedule的文章到此结束,学无止境,加油。

猜你喜欢

转载自blog.csdn.net/Jiakunboy/article/details/80713262