1、分布式调用之 quartz 基本原理

这里写目录标题

任务调度需求分析

任务调度的实现方式有很多,如果要实现我们的调度需求,我们对这个工具有什么样的基本要求呢?

基本需求

1.可以定义触发的规则,比如基于时刻、时间间隔、表达式。

2.可以定义需要执行的任务。比如执行一个脚本或者一段代码。任务和规则是分开的。

3.集中管理配置,持久配置。不用把规则写在代码里面,可以看到所有的任务配置,方便维护。重启之后任务可以再次调度------配置文件或者配置中心。

4.支持任务的串行执行,例如执行 A 任务后再执行 B 任务再执行 C 任务。

5.支持多个任务并发执行,互不干扰(例如 ScheduledThreadPoolExecutor)。

6.有自己的调度器,可以启动、中断、停止任务。

7.容易集成到 Spring。

任务调度工具对比

Timer

单线程执行

java 复制代码
import java.util.Timer;
import java.util.TimerTask;

public class TestTimerTask extends TimerTask {
    /**
     * 此计时器任务要执行的操作。
     */
    public void run() {
        Date executeTime = new Date(this.scheduledExecutionTime());
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        System.out.println("任务执行了:" + dateStr);
    }
}



public class TestTimer {
    public static void main(String[] args) {
        Timer timer = new Timer();
        TimerTask task = new TestTimerTask();
        timer.schedule(task, 5000L, 1000L);
    }
}

Quartz

基本介绍

  • 官网:http://www.quartz-scheduler.org/

  • Quatz 是一个特性丰富的,开源的任务调度库,它几乎可以嵌入所有的 Java 程序,从很小的独立应用程序到大型商业系统。Quartz 可以用来创建成百上千的简单的或者复杂的任务,这些任务可以用来执行任何程序可以做的事情。

  • Quartz 拥有很多企业级的特性,包括支持 JTA 事务和集群。

  • Quartz 是一个老牌的任务调度系统,98 年构思,01 年发布到 sourceforge。现在更新比较慢,因为已经非常成熟了。https://github.com/quartz-scheduler/quartz

  • Quartz 的目的就是让任务调度更加简单,开发人员只需要关注业务即可。他是用 Java 语言编写的(也有.NET 的版本)。Java 代码能做的任何事情,Quartz 都可以调度。

    特点:

  • 精确到毫秒级别的调度

  • 可以独立运行,也可以集成到容器中

  • 支持事务(JobStoreCMT )

  • 支持集群

  • 支持持久化

体系结构总结

JobDetail

我们创建一个实现 Job 接口的类,使用 JobBuilder 包装成 JobDetail,它可以携带KV 的数据。

Trigger

定义任务的触发规律,Trigger,使用 TriggerBuilder 来构建。

JobDetail 跟 Trigger 是 1:N 的关系。

思考:为什么要解耦?

Trigger 接口在 Quartz 有 4 个继承的子接口:

MutableTrigger 和 CoreTrigger 最终也是用到以上四个类的实现类。

SimpleTrigger

SimpleTrigger 可以定义固定时刻或者固定时间间隔的调度规则(精确到毫秒)。

例如:每天 9 点钟运行;每隔 30 分钟运行一次。

CalendarIntervalTrigger

CalendarIntervalTrigger 可以定义更多时间单位的调度需求,精确到秒。

好处是不需要去计算时间间隔,比如 1 个小时等于多少毫秒。

例如每年、每个月、每周、每天、每小时、每分钟、每秒。

每年的月数和每个月的天数不是固定的,这种情况也适用。

DailyTimeIntervalTrigger

每天的某个时间段内,以一定的时间间隔执行任务。

例如:每天早上 9 点到晚上 9 点,每隔半个小时执行一次,并且只在周一到周六执

行。

CronTrigger

CronTirgger 可以定义基于 Cron 表达式的调度规则,是最常用的触发器类型

上面我们定义的都是在什么时间执行,但是我们有一些在什么时间不执行的需求,

比如:理财周末和法定假日购买不计息;证券公司周末和法定假日休市。

是不是要把日期写在数据库中,然后读取基于当前时间判断呢?

基于 Calendar 的排除规则

如果要在触发器的基础上,排除一些时间区间不执行任务,就要用到 Quartz 的

Calendar 类(注意不是 JDK 的 Calendar)。可以按年、月、周、日、特定日期、Cron表达式排除

调用 Trigger 的 modifiedByCalendar()添加到触发器中,并且调用调度器的

addCalendar()方法注册排除规则

java 复制代码
public class CalendarDemo {
    public static void main(String[] args) throws Exception {
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler scheduler = sf.getScheduler();
        scheduler.start();

        // 定义日历
        AnnualCalendar holidays = new AnnualCalendar();

        // 排除日
        Calendar gupaoDay = (Calendar) new GregorianCalendar(2019, 8, 8);
        holidays.setDayExcluded(gupaoDay, true);
        // 排除中秋节
        Calendar midAutumn = new GregorianCalendar(2019, 9, 13);
        holidays.setDayExcluded(midAutumn, true);
        // 排除圣诞节
        Calendar christmas = new GregorianCalendar(2019, 12, 25);
        holidays.setDayExcluded(christmas, true);

        // 调度器添加日历
        scheduler.addCalendar("holidays", holidays, false, false);

        JobDetail jobDetail = JobBuilder.newJob(MyJob1.class)
                .withIdentity("job1", "group1")
                .usingJobData("gupao","测试 2673")
                .build();

        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .modifiedByCalendar("holidays")
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(2)
                        .repeatForever())
                .build();

        Date firstRunTime = scheduler.scheduleJob(jobDetail, trigger);
        System.out.println(jobDetail.getKey() + " 第一次触发: " + firstRunTime);
    }
}

Scheduler

调度器,是 Quartz 的指挥官,由 StdSchedulerFactory 产生。它是单例的。

并且是 Quartz 中最重要的 API,默认是实现类是 StdScheduler,里面包含了一个

QuartzScheduler。QuartzScheduler 里面又包含了一个 QuartzSchedulerThread。

Scheduler 中的方法主要分为三大类:

1)操作调度器本身,例如调度器的启动 start()、调度器的关闭 shutdown()。

2)操作 Trigger,例如 pauseTriggers()、resumeTrigger()。

3)操作 Job,例如 scheduleJob()、unscheduleJob()、rescheduleJob()

这些方法非常重要,可以实现任务的动态调度

Listener

我们有这么一种需求,在每个任务运行结束之后发送通知给运维管理员。那是不是

要在每个任务的最后添加一行代码呢?这种方式对原来的代码造成了入侵,不利于维护。

如果代码不是写在任务代码的最后一行,怎么知道任务执行完了呢?或者说,怎么监测

到任务的生命周期呢?

  • 观察者模式:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则

    所有依赖它的对象都会得到通知并自动更新。

  • Quartz 中提供了三种 Listener,监听 Scheduler 的,监听 Trigger 的,监听 Job 的。

    只需要创建类实现相应的接口,并在 Scheduler 上注册 Listener,便可实现对核心

    对象的监听。

JobListener
TriggerListener

SchedulerListener

方法比较多,省略

JobStore

问题:最多可以运行多少个任务(磁盘、内存、线程数)

Jobstore 用来存储任务和触发器相关的信息,例如所有任务的名称、数量、状态等

等。Quartz 中有两种存储任务的方式,一种在在内存,一种是在数据库。

RAMJobStore

Quartz 默认的 JobStore 是 RAMJobstore,也就是把任务和触发器信息运行的信息存储在内存中,用到了 HashMap、TreeSet、HashSet 等等数据结构。

如果程序崩溃或重启,所有存储在内存中的数据都会丢失。所以我们需要把这些数据持久化到磁盘。

JDBCJobStore

JDBCJobStore 可以通过 JDBC 接口,将任务运行数据保存在数据库中

JDBC 的实现方式有两种,JobStoreSupport 类的两个子类:

JobStoreTX:在独立的程序中使用,自己管理事务,不参与外部事务。

JobStoreCMT:(Container Managed Transactions (CMT),如果需要容器管理事务时,使用它。

使用 JDBCJobSotre 时,需要配置数据库信息:

java 复制代码
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 使用 quartz.properties,不使用默认配置
org.quartz.jobStore.useProperties:true
#数据库中 quartz 表的表名前缀
org.quartz.jobStore.tablePrefix:QRTZ_
org.quartz.jobStore.dataSource:myDS
#配置数据源
org.quartz.dataSource.myDS.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL:jdbc:mysql://localhost:3306/gupao?useUnicode=true&characterEncoding=utf8
org.quartz.dataSource.myDS.user:root
org.quartz.dataSource.myDS.password:123456
org.quartz.dataSource.myDS.validationQuery=select 0 from dual

问题来了?需要建什么表?表里面有什么字段?字段类型和长度是什么?

在官网的 Downloads 链接中,提供了 11 张表的建表语句:quartz-2.2.3-distribution\quartz-2.2.3\docs\dbTables

2.3 的版本在这个路径下:src\org\quartz\impl\jdbcjobstore

相关推荐
Lyqfor38 分钟前
云原生学习
java·分布式·学习·阿里云·云原生
流雨声1 小时前
2024-09-01 - 分布式集群网关 - LoadBalancer - 阿里篇 - 流雨声
分布式
floret*2 小时前
用pyspark把kafka主题数据经过etl导入另一个主题中的有关报错
分布式·kafka·etl
william8232 小时前
Information Server 中共享开源服务中 kafka 的__consumer_offsets目录过大清理
分布式·kafka·开源
P.H. Infinity2 小时前
【RabbitMQ】10-抽取MQ工具
数据库·分布式·rabbitmq
Hsu_kk5 小时前
Kafka 安装教程
大数据·分布式·kafka
苍老流年5 小时前
1. kafka分布式环境搭建
分布式·kafka
sj11637394035 小时前
Kafka参数了解
数据库·分布式·kafka
Hsu_kk5 小时前
Kafka Eagle 安装教程
分布式·kafka
CodingBrother5 小时前
Kafka 与 RabbitMQ 的联系
分布式·kafka·rabbitmq