集群环境下定时任务调度问题与方案探讨

  • 摘要spring

  • 问题:从单机扩展到集群数据库

  • 方案一:不作改造,直接扩展缓存

  • 方案二:多处调度、一处执行服务器

  • 方案三:一处调度、一处执行网络

  • 方案四:一处调度、多处执行负载均衡

  • 方案五:多处调度、多处执行分布式


摘要ide

从改造工做量、可用性、负载均衡、资源利用等方面,简单介绍了几种集群环境下定时任务调度的方案。性能


问题:从单机扩展到集群spa

单机环境的定时任务很简单。不管是用比较原始的Timer,仍是用自成体系的quartz、spring-scheduler,均可以轻松写意的实现功能。


可是,当应用水平扩展到集群环境下时, 定时任务会出现重复调度、重复执行,可能带来资源浪费、数据错误等问题。


方案一:不作改造,直接扩展

对有些定时任务来讲,重复调度、重复执行并不构成大问题。例如删除过时数据任务、数据监控任务等,除了会形成必定的资源浪费以外,其实无伤大雅。


从功能上来讲,只有严格幂等的任务才能够直接扩展。性能方面来讲,这个方案适合对网络、内存等资源压力小的定时任务。另外,若是定时任务须要处理的资源自己不在集群服务之间共享,那么必然要使用这个方案:例如检查服务本地缓存数据过时状况的定时任务就属于这种状况。


工做量

因为不须要对单机环境的定时任务进行改造,所以这个方案的改造工做量是很是小的。

只不过,在开发单机环境的定时任务时就要重点考虑如何保证任务的幂等性,这多是一项额外的工做。


可用性

高可用性是没的说的。集群中的每台服务都有一套独立、完整的任务调度、执行功能,只要有一台服务存活,就能够保证任务完整执行。


负载均衡

在负载均衡方面,这个方案是最差的:它其实是“负载翻倍”。每台服务器须要都承担一个任务的所有压力;对网络、数据库等共享资源来讲,压力更是N倍增加。因此前面才会提到:这个方案适合对网络、内存等资源性能压力小的任务。


资源利用

若是说这个方案在负载均衡方面得分为0的话,资源利用上就是负分。


方案二:多处调度、一处执行

方案一能够说是“多处调度、多处执行”,方案二则是“多处调度、一处执行”。这种方案的基本思路是:虽然多台服务同时运行调度机制,但经过某种机制来保证只有一台服务能最终执行任务。这种机制能够是quartz的集群调度功能,也能够是zookeeper的多活选主机制,还能够是分布式锁机制。

因为这个方案保证“一处执行”,所以它并不要求定时任务具备幂等性,适用性更广。


工做量

总体而言,这个方案只改造定时任务的调度机制,不涉及执行机制。而对于定时任务来讲,调度机制比较统一,执行功能则变化更多。所以,尽管工做量与实际使用的机制有关,但这个方案并不算太麻烦。

以我此前参与过的一个的定时任务多活改造为例,从使用spring-scheduler的单机调度功能改造为quartz的集群调度功能,工做量、工期、风险等都在可控范围以内。


可用性

只要实现了集群部署,可用性必定是上了一个台阶的。


不过,通常来讲,这个方案总会引入一个第三方机制来决定由哪台服务来执行任务(quartz使用数据库、zookeeper使用ZK服务等)。这个第三方机制,不管多么可靠、可用,多多少少仍是会引入一些不可用风险。例如使用zookeeper选主机制,若是由于网络、机房等缘故致使选主失败,进而使得“多处调度”以后“处执行”,也会使得定时任务引起问题。


另外,“一处执行”也会增长可用性风险。若是某个定时任务因为某台服务自身的问题执行失败,咱们须要额外的机制(如spring-batch的restart机制)来处理。


只不过这种风险几率很是低,大部分时候咱们都直接忽略掉了。


负载均衡

负载均衡是这个方案的一个“黑点”。因为只能保证“一处执行”,同一个定时任务的全部压力都在这一台服务上;其它服务即便空闲、可用,也只能袖手旁观。


可是,这个方案能够把不一样的定时任务“负载均衡”到不一样的服务上执行,从而避免全部任务都在同一台服务上执行的极端状况。


资源利用

简单分析一下,咱们能够知道,当不一样任务的资源占用(内存/cpu/网络等资源的使用量,以及资源占用时间)比较平均时,这个方案对资源的利用率比较高。可是若是各任务间相差较大(如任务A执行时间1小时,任务B执行时间2分钟),就会形成必定的资源浪费。


方案三:一处调度、一处执行

方案二的调度机制会在多台服务上同时运行,所以也能够称之为“分布调度,一处运行”。方案三则将调度机制集中到一套调度服务上,由调服服务进行“集中调度”,而后再由应用服务“一处执行”。

关于这种方案已有很多实现,如ELASTIC-JOB等等。不过其中有一些方案,其实是“多处调度、一处执行”。按下不表。


工做量

这种方案的改造量会比较大。


虽然定时任务能够分为调度和执行两部分,但大部分状况下,这两部分代码结合得都比较紧密。方案三其实是将“调度”功能与“执行”工做剥离开(有时甚至会把两者部署为不一样的服务)。拆分原有代码的工做量可见一斑。


可用性

参见方案二。


负载均衡

参见方案二。


资源利用

参见方案二。


方案四:一处调度、多处执行

方案四是方案三的一个扩展。在方案三把“调度”与“执行”拆开之后, 这个方案开始把触角伸向了“执行”功能,并将“执行”功能拆分红“读取-处理”两部分(是的,参考了spring-batch的reader-processer-writer模式)。


在方案四中,“调度”功能只是调度“读取”功能,即在指定的时间点读取出全部须要处理的数据。读出数据以后,利用消息队列等机制,将数据分给多个“处理”服务。


在咱们系统中就有相似机制。某个任务使用的是quartz的集群部署方案来保证“一处调度”;任务运行时,会先读取出当天须要处理的数据,并将其数据发送到ActiveMQ的队列中,交由相关功能来处理。


工做量

这个方案不只要把“调度”功能单独拆出来,还要把“执行”功能再次拆分为“读取”和“处理”,并分别进行部署。其中的难度可想而知。


可用性

相比方案二和方案三,这个方案引入了更多的外部依赖。因此至少理论上,它的可用性(可靠性?)是最低的。


负载均衡

这个方案的负载均衡能力来自于将“读取”到的数据分发给“处理”服务时所使用的机制。若是使用消息队列(如ActiveMQ),则它也能将任务负载均衡地分发给集群中的多台服务。


资源利用

对定时任务来讲,这个方案能够至关充分地利用系统资源。

可是,对消息队列、网络等“额外”资源来讲,这个方案在很大程度上会加剧它们的负担。


方案五:多处调度、多处执行

虽然方案一也是“多处调度、多处执行”,但方案五跟它是有差异的。


方案五将“执行”机制进一步拆分为“读取”、“判断”、“执行”三部分。首先读取定时任务所需处理的数据全集;而后利用分布式锁等机制,逐个判断读取到的某条数据是否应当由当前服务执行;只有经过了判断的数据才会进入到执行阶段。


相比“执行”,方案五对“调度”机制基本没作处理。单机环境下怎么调度,集群环境下仍然怎么调度。


工做量

方案五不变动“调度”机制,改造工做量全在“执行”机制上。而对执行机制的改造也只是“插入”一个步骤,而并不修改、拆分基本流程。这种改造工做相对来讲仍是比较轻松的。


可用性

一样实现了集群部署,方案五的可用性是有保证的。因为调度、执行都在多台服务上同时运行,不管哪一台服务出现问题,其它服务仍能正常的调度和执行任务,受到影响的可能只是那台服务上正在处理的一条(一批)数据。


而且,它对外部环境的依赖也比较小(基本也就引入了一个分布式锁),相对其它方案来讲,风险也更低。


负载均衡

懒得敲字了。


资源利用

方案五至关于把一个任务所需处理的数据平均分红N份,均匀的交给集群中的服务来处理。所以,它对资源的利用率能够说是最高的。


不过,在读取数据阶段,因为要读取一个“全集”,可能会带来一些资源压力。

相关文章
相关标签/搜索