什么是定时调度器?
我们知道程序的运行要么是由事件触发的,而这种事件的触发源头往往是用户通过ui交互操作层层传递过来的;但是我们知道还有另外一种由机器系统时间触发的程序运行场景。大家想想是否遇到或者听过这样的使用场景:
用户操作
|
--------> 程序运行
|
机器时间
机器运行资源自动定时回收。连接池管理的资源是数据库连接,连接打开后,有的可能很长时间没有使用了,而有些可能是已经因为各种因素断开连接了,这样为了销毁这些多余的活着废弃的连接,我们当然可以提供一个功能让运维管理人员来操作销毁连接,但是这增加了运维人员的工作量,而且资源状态是动态的,用户无法知道何时该操作,那岂不是要时刻检查盯着系统去操作?
所以定时调度器解决的问题是用户无法或者成本非常高而不值得操作的场景下的程序触发。而且计算机和程序的价值就在于取代重复的人工劳动,因此如果能够机器自动执行的,就应该无须人工干预。
因此我认为定时调度器就是计算机的系统时间自动触发程序运行的技术组件。这是定时的职责;另外它还应该具备调度的功能,当需要处理的任务非常多,单进程单线程处理效率无法满足需求,因此需要将任务分配给多进程和多线程并行执行,以提升并行任务处理的能力,这是调度的职责。
java定时调度器解决方案
java中的定时调度器解决方案从调度类型的角度来分类我觉得可以分为单进程和多进程两大类,单进程是指在单个进程内部实现的定时及调度,任务的执行可以分配给改进程内的多线程执行;而多进程则是在多个服务进程之间调度任务的场景,任务可以分配给多个进程和多个线程去执行。下面介绍这些在java中这两种分类都有哪些技术组建可用。
单进程定时调度器
单进程定时调度器的优势是简单易用,适用于简单的局限于应用内部的定时任务处理;它对于大量的任务单个进程处理时效性无法满足需求的场景不适应,如果多个进程同时调度处理相同任务,会出现任务执行多遍的问题,这就需要引入多线程定时调度器来协调多个进程之间的调度。
JDK Timer
jdk中自带的一个非常简单的定时任务,能够实现简单的单进程单线程定时任务。特性如下。
- jdk自带,简单易用。
- 只能单线程调度任务。多个任务之间执行是串行的,任务之间的执行相互影响。
- 只支持开始时间与重复间隔的任务调度。无法支持非常复杂的定时配置,灵活性和功能受限制。
- 定时基于绝对时间。修改系统时间对定时任务影响大。
JDK ScheduledExecutor
jdk 5.0之后的并发包中新增的一个定时调度组件。它有这样一些特性。
- jdk自带,简单易用。
- 支持通过线程池多线程调度任务。突破了jdk timer的局限性。
- 只支持开始时间与重复间隔的任务调度。无法支持非常复杂的定时配置,灵活性和功能受限制。
Quartz
quartz是一个功能非常强大的定时任务组件,它是一个第三的java技术组件,专门实现定时调度任务,项目主页是:http://quartz-scheduler.org/。它的特性是:
- 支持简单定时任务配置。
- 支持通过表达式配置复杂定时任务。灵活性和功能都更加强大。
- 支持多线程任务调度。
- 支持监听器和插件扩展功能。
- 支持嵌入式和单独部署。
- 支持集群部署。
Spring Scheduler
Spring框架也提供了对定时任务的支持,它对定时任务提供了一个抽象,如果是Spring环境下的项目需要定时任务功能,则直接基于Spring的抽象实现更好,因为面向抽闲编程以后可以随着业务的变化可以更换定时任务的实现,对于互联网项目的意义更大。那它的特性有这些:
- 提供了一套统一的定时调度器抽象。
- Spring自己提供了一套Quartz的简单实现,无须引入其它组件。
- 支持与上述Timer、Quartz等定时实现方案集成。
多进程定时调度器
多进程定时调度器相对单线程调度器来说更加复杂,概念更多,部署结构更加复杂,但它的优势是具备高可用、可动态伸缩等特性,对于具有大量待定时调度的任务来说是适合的。
taobao-pamirs-schedule
项目主页是:http://code.taobao.org/p/tbschedule/src/
阿里开源的淘宝调度2.0是一个分布式定时任务调度器,它是一个分布式去中心化的结构,默认的方式是基于数据库实现的多进程间协调。3.0开始更名为tbschedule,开始使用zookeeper实现进程协调。本系列文章重点研究2.0.它具有以下特性。
- 任务调度分配器的目标: 让所有的任务不重复,不遗漏的被快速处理
- 一个Manager只管理一种任务类型的一组工作线程。
- 在一个JVM里面可能存在多个处理相同任务类型的Manager,也可能存在处理不同任务类型的Manager。
- 在不同的JVM里面可以存在处理相同任务的Manager
- 调度的Manager可以动态的随意增加和停止
- 可以通过JMX控制调度服务的创建和停止
- 可以指定调度的时间区间
后面的文章会对该组件进行详细的使用、原理及实现的学习分析。
Elastic-Job
项目主页:https://github.com/dangdangdotcom/elastic-job
Elastic-Job是当当网开源的一个分布式定时调度器,它是基于成熟的开源产品Quartz和Zookeeper及其客户端Curator进行二次开发。应该是主要给quartz增加了多进程协调的特性。也是值得研究和学习的一个项目。
主要功能
分布式: 重写Quartz基于数据库的分布式功能,改用Zookeeper实现注册中心。
并行调度: 采用任务分片方式实现。将一个任务拆分为n个独立的任务项,由分布式的服务器并行执行各自分配到的分片项。
弹性扩容缩容: 将任务拆分为n个任务项后,各个服务器分别执行各自分配到的任务项。一旦有新的服务器加入集群,或现有服务器下线,elastic-job将在保留本次任务执行不变的情况下,下次任务开始前触发任务重分片。
集中管理: 采用基于Zookeeper的注册中心,集中管理和协调分布式作业的状态,分配和监听。外部系统可直接根据Zookeeper的数据管理和监控elastic-job。
定制化流程型任务: 作业可分为简单和数据流处理两种模式,数据流又分为高吞吐处理模式和顺序性处理模式,其中高吞吐处理模式可以开启足够多的线程快速的处理数据,而顺序性处理模式将每个分片项分配到一个独立线程,用于保证同一分片的顺序性,这点类似于Kafka的分区顺序性。
其他功能
失效转移: 弹性扩容缩容在下次作业运行前重分片,但本次作业执行的过程中,下线的服务器所分配的作业将不会重新被分配。失效转移功能可以在本次作业运行中用空闲服务器抓取孤儿作业分片执行。同样失效转移功能也会牺牲部分性能。
Spring命名空间支持: elastic-job可以不依赖于Spring直接运行,但是也提供了自定义的命名空间方便与Spring集成。
运维平台: 提供web控制台用于管理作业。
非功能需求
稳定性: 在服务器无波动的情况下,并不会重新分片;即使服务器有波动,下次分片的结果也会根据服务器IP和作业名称哈希值算出稳定的分片顺序,尽量不做大的变动。
高性能: 同一服务器的批量数据处理采用自动切割并多线程并行处理。
灵活性: 所有在功能和性能之间的权衡,都可通过配置开启/关闭。如:elastic-job会将作业运行状态的必要信息更新到注册中心。如果作业执行频度很高,会造成大量Zookeeper写操作,而分布式Zookeeper同步数据可能引起网络风暴。因此为了考虑性能问题,可以牺牲一些功能,而换取性能的提升。
一致性: elastic-job可牺牲部分性能用以保证同一分片项不会同时在两个服务器上运行。
容错性: 作业服务器和Zookeeper断开连接则立即停止作业运行,用于防止分片已经重新分配,而脑裂的服务器仍在继续执行,导致重复执行。
参考资料
1.http://blog.csdn.net/heyutao007/article/details/38797335
2.http://blog.csdn.net/javafay/article/details/8031269
3.各项目主页介绍等。
后续
后续将展开对项目taobao-parims-schedule2.0的原理及源码的分析,因为我们工作中使用该项目较多。平时遇到的问题也较多。我们的使用场景是每天要多日增300万的业务数据进行任务处理,是一个非常大的规模的定时调度器使用场景,另外对任务处理的时效性也有较高要求。