Spring 整合 Quartz 分布式调度
前言
最近在做项目时,项目中涉及到很多定时任务相关的功能,并且很多定时都是动态的。为了业务开发时定时调度更加方便,且能清晰的管理所有定时任务,我决定将调度中心这个模块做成可视化的管理界面;此外,考虑到后期项目的壮大,应用的高可用和高并发性,可能会有采用集群部署多个节点;对于定时任务,如果每个节点都执行自己的定时任务,一方面耗费了系统资源,另一方面有些任务多次执行,可能引发应用逻辑问题,所以需要一个分布式的调度系统,来协调每个节点执行定时任务,定时任务采用动态配置并持久化到数据库。
版本选择
spring 对于 quartz 的支持,是通过 org.springframework.scheduling.quartz.CronTriggerBean 继承 org.quartz.CronTrigger 来实现的。在 quartz1.x 系列中 org.quartz.CronTrigger 是个类,而在 quartz2.x 系列中 org.quartz.CronTrigger 变成了接口,这就造成了无法用 spring 的方式配置 quartz 的触发器(trigger)。因此,在 spring3.1 以下的版本必须使用 quartz1.x 系列,3.1 以上的版本才支持 quartz 2.x,否则会出错。 本次采用版本:spring 版本 4.3.5.RELEASE,quartz 版本 2.3.0
Spring 整合 Quartz
Maven 依赖文件
1 | <dependencies> |
配置 job
Quartz 是一个成熟的任务调度系统提供了两种方式来配置 job,分别是:
- MethodInvokingJobDetailFactoryBean
- JobDetailFactoryBean
MethodInvokingJobDetailFactoryBean
要调用特定 bean 的一个方法的时候使用,具体配置如下:
1 | <bean id="firstTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> |
JobDetailFactoryBean
这种方式更加灵活,可以设置传递参数,具体配置如下:
1 | <bean id="springFrameworkQuartzJobDemoBeanId" |
jobClass 定义的任务类,继承 QuartzJobBean,实现 executeInternal 方法;可以使用 jobDataMap 来给 job 传递数据;
配置调度触发器
调度的触发器同样也提供了两种类型,分别是:
- SimpleTriggerFactoryBean
- CronTriggerFactoryBean
CronTriggerFactoryBean 相对更加灵活,本例中也是采用这种类型的触发器,如下:
1 | <!-- 执行定时器 --> |
配置 Quartz 调度器的 SchedulerFactoryBean
Quartz 调度器的 SchedulerFactoryBean 同样也提供了两种方式:
- 内存 RAMJobStore
- 数据库方式
RAMJobStore
job 的相关信息存储在内存里,每个节点存储各自的,互相隔离,配置如下:
1 | <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false"> |
数据库方式
job 的相关信息存储在数据库中,所有节点共用数据库,每个节点通过数据库来通信,保证一个 job 同一时间只会在一个节点上执行,并且如果某个节点挂掉,job 会被分配到其他节点执行,这也是集群部署时,分布式的调度系统采用的方式。
其原理如下:
具体配置如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">
<property name="dataSource" ref="dataSource" />
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.instanceName">EBMDynamicQuartz</prop>
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
<!-- 线程池配置 -->
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
<prop key="org.quartz.threadPool.threadCount">20</prop>
<prop key="org.quartz.threadPool.threadPriority">5</prop>
<!-- JobStore 配置 -->
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<!-- 集群配置 -->
<prop key="org.quartz.jobStore.isClustered">true</prop>
<prop key="org.quartz.jobStore.clusterCheckinInterval">15000</prop>
<prop key="org.quartz.jobStore.maxMisfiresToHandleAtATime">1</prop>
<prop key="org.quartz.jobStore.misfireThreshold">120000</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
</props>
</property>
<property name="schedulerName" value="EBMDynamicQuartz" />
<!--必须的,QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动 -->
<property name="startupDelay" value="30" />
<property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
<!--可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 -->
<property name="overwriteExistingJobs" value="true" />
<!-- 设置自动启动 -->
<property name="autoStartup" value="true" />
<!--<property name="configLocation" value="classpath:quartz.properties" />-->
</bean>
dataSource 用来配置数据源,数据表相关信息,可以到 quartz 官网下载 gz 包。面提供了主流数据库的 sql 文件,总共 11 张表,本例采用 mysql 数据库,表结构如下:
1 | # |
附:本次项目地址
https://github.com/syshlang/syshlang-spring-quartz.git