天天看点

datax 定时执行多个job_分布式定时任务框架Quartz

前言

项目中总要写那么几个定时任务来处理一些事情。一些简单的定时任务使用Spring自带的定时任务就能完成。但是如果需要大量的定时任务的话要怎么才能统一管理呢?

本文介绍Quartz分布式调度框架。

介绍

Quartz介绍

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,是完全由java开发的一个开源的任务日程管理系统。目前是 Terracotta 旗下的一个项目。官网地址  http://www.quartz-scheduler.org/ 可以 下载 Quartz 的发布版本及其源代码。

特点

  • 集成方便(完全使用Java编写)
  • 无需依赖可集群部署也可单机运行
  • 可以通过JVM独立运行

Job

创建一个任务只需要实现Job接口即可

datax 定时执行多个job_分布式定时任务框架Quartz

触发器

  • 可以通过 Calendar 执行(排除节假日)
  • 指定某个时间无线循环执行 比如每五分钟执行一次
  • 固定时间执行 例如每周周一上午十点执行

一般情况使用SimpleTrigger,和CronTrigger,这些触发器实现了Trigger接口。或者 ScheduleBuilder 子类 SimpleScheduleBuilder和CronScheduleBuilder。

对于简单的时间来说,比如每天执行几次,使用SimpleTrigger。对于复杂的时间表达式来说,比如每个月15日上午几点几分,使用CronTrigger以及CromExpression 类。

注意 :一个job可以被多个Trigger 绑定,但是一个Trigger只能绑定一个job

存储

有两种存储方式 RAMJobStore和 JDBCJobStore 。

RAMJobStore不需要外部数据库调度信息存储在JVM内存中 所以,当应用程序停止运行时,所有调度信息将被丢失存储多少个Job和Trigger也会受到限制。

JDBCJobStore 支持集群所有触发器和job都存储在数据库中无论服务器停止和重启都可以恢复任务同时支持事务处理。

实战

准备

上面简单的介绍了一下Quartz,然后现在开始实战,本文使用SpringBoot整合。

项目地址:https://gitee.com/lqlm/toolsList_lqcoder

首先创建数据库表因为太多了就不放在文章中了可以去官方网站下载,也可以用我的下载地址下载

地址:https://lqcoder.com/quartz.sql

创建完成之后:

datax 定时执行多个job_分布式定时任务框架Quartz
Table Name Description
QRTZ_CALENDARS 存储Quartz的Calendar信息
QRTZ_CRON_TRIGGERS 存储CronTrigger,包括Cron表达式和时区信息
QRTZ_FIRED_TRIGGERS 存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息
QRTZ_PAUSED_TRIGGER_GRPS 存储已暂停的Trigger组的信息
QRTZ_SCHEDULER_STATE 存储少量的有关Scheduler的状态信息,和别的Scheduler实例
QRTZ_LOCKS 存储程序的悲观锁的信息
QRTZ_JOB_DETAILS 存储每一个已配置的Job的详细信息
QRTZ_JOB_LISTENERS 存储有关已配置的JobListener的信息
QRTZ_SIMPLE_TRIGGERS 存储简单的Trigger,包括重复次数、间隔、以及已触的次数
QRTZ_BLOG_TRIGGERS Trigger作为Blob类型存储
QRTZ_TRIGGER_LISTENERS 存储已配置的TriggerListener的信息
QRTZ_TRIGGERS 存储已配置的Trigger的信息

本文统一使用Cron方式来创建。

注意:cron方式需要用到的4张数据表:qrtz_triggers,qrtz_cron_triggers,qrtz_fired_triggers,qrtz_job_details。

整合项目

创建一个SpringBoot项目然后加入quartz依赖,同时也要加入c3p0的依赖因为quartz使用的数据库是和项目分开的。

同时在resources下创建quartz.properties文件内容

然后创建一个Job类

创建一个工具类然后进行定时任务的增删改

首先创建一个调度工厂

添加定时任务

public static void addJob(String jobName, String jobGroupName,
                              String triggerName, String triggerGroupName, Class jobClass, String cron) {
try {
            Scheduler sched = schedulerFactory.getScheduler();
// 任务名,任务组,任务执行类
//            Trigger.TriggerState state = sched.getTriggerState();

            JobDetail jobDetail=  JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
// 触发器
            TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();// 触发器名,触发器组
            triggerBuilder.withIdentity(triggerName, triggerGroupName);
            triggerBuilder.startNow();// 触发器时间设定
            triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));// 创建Trigger对象
            CronTrigger trigger = (CronTrigger) triggerBuilder.build();// 调度容器设置JobDetail和Trigger
            sched.scheduleJob(jobDetail, trigger);// 启动if (!sched.isShutdown()) {
                sched.start();
            }
        } catch (Exception e) {throw new RuntimeException(e);
        }
    }
           

创建流程

通过工厂获取 Scheduler对象

Scheduler sched = schedulerFactory.getScheduler();

设置Job的实现类和一些静态信息

构建触发器

// 触发器
TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();
 // 触发器名,触发器组
triggerBuilder.withIdentity(triggerName, triggerGroupName);
triggerBuilder.startNow();
// 触发器时间设定
triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
// 创建Trigger对象
CronTrigger trigger = (CronTrigger) triggerBuilder.build();
           

然后把Job和触发器都设置到Scheduler对象中

启动

运行

因为使用的是SpringBoot项目所以就直接在启动类加入添加定时任务

参数分别为:JobName JsobgropName 中间省略 实现类,任务执行时间

然后查看输出日志:

datax 定时执行多个job_分布式定时任务框架Quartz

可以看到已经在执行了,现在我们去看一下数据库中的数据要查看的表有qrtz_triggers,qrtz_cron_triggers,qrtz_fired_triggers,qrtz_job_details。

qrtz_job_details:

datax 定时执行多个job_分布式定时任务框架Quartz

已经存在

现在把项目停止然后在重新启动会发生什么?

datax 定时执行多个job_分布式定时任务框架Quartz

发现抛出了异常,因为我们已经添加过这个定时任务了所以重复添加是行不通的。

这时候我们直接启动即可。

同样封装启动方法

启动所有定时任务非常简单直接获取Scheduler对象然后start即可。

修改定时任务

修改定时任务同样需要获取Scheduler对象,和添加流程基本一致,只不过最后不是调用的scheduleJob()而是调用的rescheduleJob()方法.有两种方式都需要指定定时器名称

  • 第一种是调用rescheduleJob()直接修改
  • 第二种是先删除然后在新增
/**
     * @Description: 修改一个任务的触发时间
     *
     * @param jobName
     * @param jobGroupName
     * @param triggerName 触发器名
     * @param triggerGroupName 触发器组名
     * @param cron   时间设置,参考quartz说明文档
     */
public static void modifyJobTime(String jobName,
                                     String jobGroupName, String triggerName, String triggerGroupName, String cron) {
try {
            Scheduler sched = schedulerFactory.getScheduler();
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
            }

            String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(cron)) {
/** 方式一 :调用 rescheduleJob 开始 */
// 触发器
                TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();// 触发器名,触发器组
                triggerBuilder.withIdentity(triggerName, triggerGroupName);
                triggerBuilder.startNow();// 触发器时间设定
                triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));// 创建Trigger对象
                trigger = (CronTrigger) triggerBuilder.build();// 方式一 :修改一个任务的触发时间
                sched.rescheduleJob(triggerKey, trigger);/** 方式一 :调用 rescheduleJob 结束 *//** 方式二:先删除,然后在创建一个新的Job  *///JobDetail jobDetail = sched.getJobDetail(JobKey.jobKey(jobName, jobGroupName));//Class extends Job> jobClass = jobDetail.getJobClass();//removeJob(jobName, jobGroupName, triggerName, triggerGroupName);//addJob(jobName, jobGroupName, triggerName, triggerGroupName, jobClass, cron);/** 方式二 :先删除,然后在创建一个新的Job */
            }
        } catch (Exception e) {throw new RuntimeException(e);
        }
    }
           

删除任务

删除定时任务在修改的时候已经有实例.注意都需要指定任务名称 任务分组和触发器名称触发器分组

传递参数

现在还有一个问题就是我想把参数传递到Job实现类里面咋整?

在添加定时任务时,创建JobDetail的时候有一个setJobData()方法参数为JobDataMap,看下JobBuilder源码

datax 定时执行多个job_分布式定时任务框架Quartz

可以看到JobBuilder提供了setJobData方法传递的参数为JobDataMap是Map类型.

在创建定时任务的时候可以:

然后在Job实现类方法中直接取

JobDataMap可以直接当作Map进行操作.

单个参数可以使用usingJobData()来添加,参数为K V 取值方法一致,同时参数也是持久化到数据库的

如果需要查询管理的话可以直接查询数据库

原理解析

上面简单的介绍了一下怎么使用,那么你一定对它是怎么运行的感兴趣.接下来就分析一下Quartz到底是怎么实现的

注意到上面增删改都要先通过schedulerFactory工厂(工厂模式)来先获取Scheduler实例,现在就从第一步开始分析

本文就简单分析一下Scheduler工厂和添加定时任务这两步骤.

Scheduler工厂

添加定时任务

- End -

datax 定时执行多个job_分布式定时任务框架Quartz

Redis是如何实现点赞、取消点赞的?

阿里巴巴为什么能抗住90秒100亿?看完这篇你就明白了!

超详细:如何设计出健壮的秒杀系统?

我是如何用 Redis 做实时订阅推送的?

datax 定时执行多个job_分布式定时任务框架Quartz

继续阅读