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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了集群环境下定时任务调度问题与方案探讨相关的知识,希望对你有一定的参考价值。

  • 摘要

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

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

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

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

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

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


摘要

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


问题:从单机扩展到集群

单机环境的定时任务很简单。无论是用比较原始的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份,均匀的交给集群中的服务来处理。因此,它对资源的利用率可以说是最高的。


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


本文出自 “编程的摩羯男” 博客,请务必保留此出处http://winters1224.blog.51cto.com/3021203/1963788

以上是关于集群环境下定时任务调度问题与方案探讨的主要内容,如果未能解决你的问题,请参考以下文章

Spring+Quartz集群环境下定时调度的解决方案

Java技术分享:集群环境下的定时任务

分布式集群架构场景优化解决方案:分布式调度问题

分布式集群架构场景优化解决方案:分布式调度问题

为社么要用分布式集群任务调度?

集群环境下如何防止定时任务重复执行?