likes
comments
collection
share

翻翻Quartz框架的旧账

作者站长头像
站长
· 阅读数 26

翻翻Quartz框架的旧账


前言

Quartz是一个基于Java开发的 分布式定时任务框架,在灵活且强大的任务调度能力的基础上还具备分布式能力。本文将对这个经典且好用的定时任务框架进行概念入门,并给出基于原生Quartz框架的示例工程。

正文

一. Quartz基本概念

Quartz的核心功能就是管理调度 定时任务并执行。在Quartz中,任务Job组件表示,代表要定时执行的业务逻辑,而 定时则由Trigger组件表示,代表定时的时间规则,除此之外,还有一个组件叫做Scheduler负责全局的JobTrigger的管理调度。

所以,Quartz中,核心的组件就是JobTriggerScheduler,这三者关系可以用下图进行示意。

翻翻Quartz框架的旧账

特别要注意,一个Job可以被多个Trigger所关联,但一个Trigger只能关联一个Job

围绕JobTriggerSchedulerQuartz构建了如下的一套完整的定时任务架构体系。

翻翻Quartz框架的旧账

下面分别介绍一下上图中出现的组件。

  1. Job。表示定时任务的具体的任务内容,也就是 要作什么Quartz中提供了Job接口,我们定义的任务需要实现Job接口;
  2. JobDetailJobDetail有一个jobClass的类字段,表示其绑定的Job的类对象,此外JobDetailJob之上扩展提供了很多属性,其中很重要的就是JobDetailnamegroup字段来唯一标识自己,因此Quartz中在存储和调度任务时,实际的操作对象是JobDetail
  3. JobBuilder。用于创建JobDetail
  4. Trigger。表示任务的定时规则,称为触发器,也就是 什么时候做,做多少次Quartz中定义了Trigger接口,Trigger接口要求所有的触发器,都需要有 开始时间结束时间,也就是起止时间是需要有的,此外Trigger接口有两个常用的子接口,分别为SimpleTriggerCronTrigger,其中SimpleTriggerTrigger基础上,可以额外指定 执行次数相邻执行的间隔来决定定时规则,而CronTriggerTrigger基础上可以额外指定cron表达式来决定定时规则;
  5. TriggerBuilder。用于创建Trigger
  6. Scheduler和SchedulerFactorySchedulerFactory用于创建SchedulerScheduler是一个接口,其有三个实现,分别是JBoss4RMIRemoteMBeanSchedulerRemoteSchedulerStdScheduler,常用的是StdScheduler,但无论是哪种Scheduler其实都不重要,因为Scheduler的三个实现其实都是一个壳子,真正的管理调度是交给其持有的QuartzScheduler来完成;
  7. QuartzScheduler。真正的调度器,用于管理调度JobDetailTrigger。所谓管理,QuartzScheduler可以完成JobDetailTrigger增删改查,所谓调度,通过QuartzScheduler可以决定在哪个实例上触发哪些Trigger来执行哪些定时任务;
  8. ThreadPool。由QuartzScheduler持有的一个线程池。当某一个定时任务满足条件要执行了,调度器会为这个定时任务从ThreadPool中分配一个线程来执行这个任务;
  9. JobStore。决定Quartz中的JobDetailTrigger如何存储。JobStore接口有两个常用实现,分别是JobStoreSupportRAMJobStore,其中JobStoreSupport会将JobDetailTrigger存储到数据库,RAMJobStore会将JobDetailTrigger存储到内存;
  10. QuartzSchedulerThread。由QuartzScheduler持有的 调度线程。在QuartzScheduler创建出来并被调用start() 方法后,QuartzSchedulerThread就会开始运行,会不断的去判断哪些Trigger到点需要触发了,需要触发的Trigger就会被从ThreadPool中分配一个线程,然后执行Trigger关联的JobDetail
  11. listeners。是Quartz提供的监听器机制,具体有三种监听器,分别为JobListenerTriggerListenerSchedulerListener,其中常用的是JobListener

最后说明一下JobDetailTrigger的关系。在Quartz中,JobDetailTrigger都有namegroup这两个字段,这两个字段就能唯一标识一个JobDetailTrigger,此时Trigger会通过JobDetailnamegroup来关联一个JobDetail,当Trigger的定时规则满足时,Trigger就会触发,此时Trigger关联的任务就会被执行,此时也称任务满足定时条件被执行了。

二. Quartz的使用场景

说到定时任务,我们首先会想到使用ScheduledThreadPoolExecutor,或者使用Spring的@Scheduled注解,这种场景下,定时任务的执行是单点的,定时任务的信息是在内存中的,Quartz的最简单的使用场景就是这种场景。

如果再进阶一点,我们可以使用Quartz将定时任务的信息进行持久化,就算实例发生故障重启,也不会造成定时任务信息丢失,此时定时任务的执行仍旧是单点的,但是定时任务的信息却完成了持久化。

上述两种场景其实在生产中不怎么使用,使用Quartz更多的是想要实现 应用多实例部署时的定时任务单点执行。什么意思呢,意思就是应用如果有多个实例,那么当定时任务要执行时,仅由多个实例中的其中一个实例来执行任务,这样一来可以防止定时任务重复执行,二来可以防止单点故障耽搁定时任务的执行。此外,Quartz在此基础上,还能提供多实例下的定时任务启停,misfire补偿等额外功能。

三. Quartz的使用入门

本文将基于原生Quartz来演示多实例时的定时任务执行。

首先在pom中引入如下依赖。

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.31</version>
</dependency>

然后创建一个Job接口的实现类。

public class HelloJob implements Job {

    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println(LocalDateTime.now().toString() + " Hello");
    }

}

如下测试方法完成JobDetailTrigger的创建和注册。

public class AddJobAndTrigger {

    public static final String JOB_NAME_1 = "Job-Name-1";
    public static final String JOB_GROUP_1 = "Job-Group-1";

    public static final String TRIGGER_NAME_1 = "Trigger-Name-1";
    public static final String TRIGGER_GROUP_1 = "Trigger-Group-1";

    public static void main(String[] args) throws Exception {
        // 创建调度器
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 创建JobDetail
        JobDetail jobDetail = JobBuilder
                .newJob(HelloJob.class)
                .withIdentity(JOB_NAME_1, JOB_GROUP_1)
                .build();
        // 创建Trigger
        Trigger trigger = TriggerBuilder
                .newTrigger()
                .withIdentity(TRIGGER_NAME_1, TRIGGER_GROUP_1)
                .startNow()
                .withSchedule(SimpleScheduleBuilder
                        .simpleSchedule()
                        .withIntervalInSeconds(10)
                        .repeatForever())
                .build();
        // 注册JobDetail和Trigger
        scheduler.scheduleJob(jobDetail, trigger);
    }

}

如下测试方法开启调度器。

public class ClusterTest {

    public static void main(String[] args) throws Exception {
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        scheduler.start();
    }

}

因为定时任务信息要持久化到数据库且要实现多实例调度,所以还需要添加如下的配置文件,配置文件名字需要是quartz.properties并放在classpath下,Quartz框架会自己去读取。

# 区分不同的调度器
# 该配置相同的实例才会进行分布式定时调度
org.quartz.scheduler.instanceName = MyScheduler
# 自动设置实例ID
org.quartz.scheduler.instanceId = AUTO

# 数据保存方式设置为持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 数据源名
org.quartz.jobStore.dataSource = quartzDataSource
# 是否分布式化
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 5000

# 线程池配置
org.quartz.threadPool.threadCount = 3
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

# 配置数据源信息
org.quartz.dataSource.quartzDataSource.driver = com.mysql.cj.jdbc.Driver
org.quartz.dataSource.quartzDataSource.URL = 数据库连接串
org.quartz.dataSource.quartzDataSource.user = 数据库用户名
org.quartz.dataSource.quartzDataSource.password = 数据库用户密码
org.quartz.dataSource.quartzDataSource.maxConnections = 5

最后在源码的org.quartz.impl.jdbcjobstore目录下找到tables_mysql_innodb.sql并执行,为数据库添加Quartz框架需要的表。

此时先运行AddJobAndTrigger测试程序,完成JobDetailTrigger的注册,然后运行ClusterTest测试程序,开启实际的任务调度并执行。

总结

Quartz的核心就是任务Job,触发器Trigger和调度器Scheduler

对于任务Job,实际代表的是 做什么Quartz中操作任务时实际是操作和Job关联的JobDetailJobDetail可以通过namegroup进行唯一确定。

对于触发器Trigger,实际代表的是 什么时候做,做多久,做几次。每个Trigger也可以通过namegroup进行唯一确定,且每个Trigger都会关联一个JobDetail,当Trigger定时规则满足被触发时,其关联的JobDetail就会被执行。

对于调度器Scheduler,负责JobDetailTrigger的增删改查,以及决定在哪个实例上触发哪些Trigger来执行哪些定时任务。Quartz中真正发挥作用的调度器是QuartzScheduler,其它的各种调度器都是对QuartzScheduler的包装,真正干活儿的还是QuartzScheduler

转载自:https://juejin.cn/post/7375345026429222923
评论
请登录