在上篇文章中,我们详细的介绍了 Quartz 的架构原理与单体应用,今天我们一起来分享一下 quart 的分布式调度和实践
一、摘要
阅读完本文大概需要 10 分钟,本文主要分享内容如下:
- springboot + quartz + mysql 实现持久化分布式调度
- 集群环境任务调度测试
二、Quartz 集群架构
Quartz 是 Java 领域最著名的开源任务调度工具。
在上篇文章中,我们详细的介绍了 Quartz 的单体应用实践,如果只在单体环境中应用,Quartz 未必是最好的选择,例如Spring Scheduled
一样也可以实现任务调度,并且与SpringBoot
无缝集成,支持注解配置,非常简单,但是它有个缺点就是在集群环境下,会导致任务被重复调度!
而与之对应的 Quartz 提供了极为广用的特性,如任务持久化、集群部署和分布式调度任务等等,正因如此,基于 Quartz 任务调度功能在系统开发中应用极为广泛!
在集群环境下,Quartz 集群中的每个节点是一个独立的 Quartz 应用,没有负责集中管理的节点,而是通过数据库表来感知另一个应用,利用数据库锁的方式来实现集群环境下进行并发控制,每个任务当前运行的有效节点有且只有一个!
特别需要注意的是:分布式部署时需要保证各个节点的系统时间一致!
下面我们一起来看看具体的应用实践!
三、数据表初始化
数据库表结构官网已经提供,我们可以直接访问Quartz
对应的官方网站,找到对应的版本,然后将其下载!
小编我选择的是quartz-2.3.0-distribution.tar.gz
,下载完成之后将其解压,在文件中搜索sql
,在里面选择适合当前环境的数据库脚本文件,然后将其初始化到数据库中即可!
例如,我使用的数据库是mysql-5.7
,因此我选择的是tables_mysql_innodb.sql
脚本,具体内容如下:
1 |
|
具体表描述如下:
表名 | 描述 |
---|---|
QRTZ_BLOG_TRIGGERS | Trigger作为Blob类型存储 |
QRTZ_CALENDARS | 存储Quartz的Calendar信息 |
QRTZ_CRON_TRIGGERS | 存储CronTrigger,包括Cron表达式和时区信息 |
QRTZ_FIRED_TRIGGERS | 存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息 |
QRTZ_JOB_DETAILS | 存储每一个已配置的Job的详细信息 |
QRTZ_LOCKS | 存储程序的悲观锁的信息 |
QRTZ_PAUSED_TRIGGER_GRPS | 存储已暂停的Trigger组的信息 |
QRTZ_SCHEDULER_STATE | 存储少量的有关Scheduler的状态信息,和别的Scheduler实例 |
QRTZ_SIMPLE_TRIGGERS | 存储简单的Trigger,包括重复次数、间隔、以及已触的次数 |
QRTZ_SIMPROP_TRIGGERS | 存储CalendarIntervalTrigger和DailyTimeIntervalTrigger两种类型的触发器 |
QRTZ_TRIGGERS | 存储已配置的Trigger的信息 |
其中,QRTZ_LOCKS 就是 Quartz 集群实现同步机制的行锁表!
四、Quartz 集群实践
4.1、创建springboot项目,导入maven依赖包
1 |
|
4.2、创建 application.properties 配置文件
1 |
|
4.3、创建 quartz.properties 配置文件
1 |
|
4.4、注册 Quartz 任务工厂
1 |
|
4.5、注册调度工厂
1 |
|
4.6、重新设置 Quartz 数据连接池
默认 Quartz 的数据连接池是 c3p0,由于性能不太稳定,不推荐使用,因此我们将其改成driud
数据连接池,配置如下:
1 |
|
创建完成之后,还需要在quartz.properties
配置文件中设置一下即可!
1 |
|
如果已经配置,请忽略!
4.7、编写 Job 具体任务类
1 |
|
4.8、编写 Quartz 服务层接口
1 |
|
对应的实现类QuartzJobServiceImpl
如下:
1 |
|
4.9、编写 contoller 服务
- 先创建一个请求参数实体类
1 |
|
- 编写 web 服务接口
1 |
|
4.10、服务接口测试
运行 SpringBoot 的Application
类,启动服务!
- 创建一个每5秒钟执行一次的定时任务
可以看到服务正常运行!
4.11、注册监听器(选用)
当然,如果你想在 SpringBoot 里面集成 Quartz 的监听器,操作也很简单!
- 创建任务调度监听器
1 |
|
- 创建任务触发监听器
1 |
|
- 创建任务执行监听器
1 |
|
- 最后,将监听器注册到
Scheduler
1 |
|
4.12、采用项目数据源(选用)
在上面的 Quartz 数据源配置中,我们使用了自定义的数据源,目的是和项目中的数据源实现解耦,当然有的同学不想单独建库,想和项目中数据源保持一致,配置也很简单!
- 在
quartz.properties
配置文件中,去掉org.quartz.jobStore.dataSource
配置
1 |
|
- 在
QuartzConfig
配置类中加入dataSource
数据源,并将其注入到quartz
中
1 |
|
五、任务调度测试
在实际的部署中,项目都是集群进行部署,因此为了和正式环境一致,我们再新建两个相同的项目来测试一下在集群环境下 quartz 是否可以实现分布式调度,保证任何一个定时任务只有一台机器在运行?
理论上,我们只需要将刚刚新建好的项目,重新复制一份,然后修改一下端口号就可以实现本地测试!
因为curd
服务只需要一个,因此我们不需要再编写QuartzJobService
等增、删、改服务,仅仅保持QuartzConfig
、DruidConnectionProvider
、QuartzJobFactory
、TfCommandJob
、quartz.properties
类和配置都是相同的就可以了!
- 依次启动服务
quartz-001
、quartz-002
、quartz-003
,看看效果如何
第一个启动的服务
quartz-001
会优先加载数据库中已经配置好的定时任务,其他两个服务quartz-002
、quartz-003
都没有主动调度服务
- 当我们主动关闭
quartz-001
,quartz-002
服务主动接收任务调度
- 当我们主动关闭
quartz-002
,同样quartz-003
服务主动接收任务调度
最终结果,和我们预期效果一致!
六、小结
本文主要围绕springboot + quartz + mysql
实现持久化分布式调度进行介绍,所有的代码功能,笔者都亲自测试过,可能内容比较多,鉴于笔者才疏学浅,如果有遗漏的地方,欢迎网友批评指出!