浅谈Quartz

一、概述

Quartz点我跳转至官网)是一个开放源码的任务调度框架。Quartz功能强大,可以让你的程序在指定时间执行,也可以按照某一个频度执行,支持数据库、监听器、插件、集群等特性。

使用场景:定时消息推送、定时抢购、定时发送邮件、定时统计等。

二、环境搭建

Maven坐标
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.1</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.2.1</version>
</dependency>
定义任务内容
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.Date;

/**
 * 任务内容
 */
public class MyJob implements Job{

    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        // 输出系统当前时间
        System.out.println(new Date());
    }
}
构建调度任务
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

/**
 * 构建调度任务
 */
public class QuartzTest {

    public static void main(String[] args) throws SchedulerException {
        // 获取调度器
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 包装任务内容
        JobDetail job = newJob(MyJob.class)
                .withIdentity("job1", "group1")
                .build();
        // 定义触发器
        Trigger trigger = newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .withSchedule(simpleSchedule()
                        .withIntervalInSeconds(40)
                        .repeatForever())
                .build();
        // 组装任务
        scheduler.scheduleJob(job, trigger);

        // 启动调度器 开始调度
        scheduler.start();
    }
}

三、体系架构

  • Job

    ​ 是一个接口,只定义一个方法execute(JobExecutionContext context),在实现接口的execute方法中编写所需要定时执行的Job(任务),JobExecutionContext类提供了调度应用的一些信息。Job运行时的信息保存在JobDataMap实例中。

  • JobDetail

    ​ JobDetail 定义的是任务数据,而真正的执行逻辑是在Job中。sheduler每次执行,都会根据JobDetail创建一个新的Job实例。

  • Trigger

    Trigger是一个类,描述触发Job执行的时间触发规则。主要有SimpleTriggerCronTrigger这两个子类。当且仅当需调度一次或者以固定时间间隔周期执行调度,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如工作日周一到周五的15:00~16:00执行调度等

    Cron表达式的格式:秒 分 时 日 月 周 年(可选)。

    字段名允许的值允许的特殊字符
    0-59, – * /
    0-59, – * /
    0-23, – * /
    1-31, – * ? / L W C
    1-12 or JAN-DEC, – * /
    1-7 or SUN-SAT, – * ? / L C # MON FRI
    年(可选字段)empty, 1970-2099, – * /

    允许的特殊字符:

    1. “?”字符:表示不确定的值

    2. “,”字符:指定数个值

    3. “-”字符:指定一个值的范围

    4. “/”字符:指定一个值的增加幅度。n/m表示从n开始,每次增加m

    5. “L”字符:用在日表示一个月中的最后一天,用在周表示该月最后一个星期X

    6. “W”字符:指定离给定日期最近的工作日(周一到周五)

    7. “#”字符:表示该月第几个周X。6#3表示该月第3个周五

    Cron表达式范例:

    1. 每隔5秒执行一次:*/5 * * * * ?
    2. 每隔1分钟执行一次:0 */1 * * * ?
    3. 每天23点执行一次:0 0 23 * * ?
    4. 每天凌晨1点执行一次:0 0 1 * * ?
    5. 每月1号凌晨1点执行一次:0 0 1 1 * ?
    6. 每月最后一天23点执行一次:0 0 23 L * ?
    7. 每周星期天凌晨1点实行一次:0 0 1 ? * L
    8. 在26分、29分、33分执行一次:0 26,29,33 * * * ?
    9. 每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
  • Scheduler

    ​ 代表一个Quartz的独立运行容器, Trigger和JobDetail可以注册到Scheduler中, 两者在Scheduler中拥有各自的组及名称, 组及名称是Scheduler查找定位容器中某一对象的依据, Trigger的组及名称必须唯一, JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法, 允许外部通过组及名称访问和控制容器中Trigger和JobDetail。

  • JobBulider

    ​ 用于定义/构建已经定义了Job实例的JobDetail实例

  • TriggerBuilder

    ​ 用于定义/构建Trigger实例。

四、Spring集成

Maven坐标
 <!-- quartz依赖 -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.1</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.2.1</version>
</dependency>

<!--spring依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>3.2.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>3.2.8.RELEASE</version>
</dependency>
定义任务内容
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.Date;

public class MyJob implements Job{

    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println(new Date());
    }
}
配置文件

如:spring-quartz.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--创建JobDetail-->
    <bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> 
        <!--指定任务类-->
        <property name="jobClass" value="MyJob"></property> 
        <!--当Job在没有可以使用的trigger的情况下 不删除-->
        <property name="durability" value="true"></property>
    </bean> 
    <!--注意 spring quartz整合 一个trigger只可以绑定一个JobDetail 一个jobDetail可以被多个Trigger所使用-->
    <bean id="trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <!--绑定JobDetail-->
        <property name="jobDetail" ref="jobDetail"></property>
        <property name="cronExpression" value="0-30 * * * * ?"></property>
    </bean>
   
    <bean id="trigger1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> 
        <!--绑定JobDetail-->
        <property name="jobDetail" ref="jobDetail"></property>
        <property name="cronExpression" value="45-55 * * * * ?"></property>
    </bean>
    <!--注册trigger-->
    <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="trigger"></ref>
                <ref bean="trigger1"></ref>
            </list>
        </property>
    </bean>
</beans>
测试

启动spring工厂测试

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class QuartzTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    }
}
运行结果如图所示:

在这里插入图片描述

五、存储方式

RAMJobStoreJDBCJobStore
类型优点缺点
RAMJobStore(默认不要外部数据库,配置容易,运行速度快因为调度程序信息是存储在被分配给JVM的内存里面,所以,当应用程序停止运行时,所有调度信息将被丢失。另外因为存储到JVM内存里面,所以可以存储多少个Job和Trigger将会受到限制
JDBCJobStore支持集群,因为所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务运行速度的快慢取决与连接数据库的快慢
设置 JDBCJobStore

​ 在应用程序中设置使用 JDBCJobStore 需要两步:首先必须创建作业仓库使用的数据库表。JDBCJobStore 与所有主流数据库都兼容,而且 Quartz 提供了一系列创建表的 SQL 脚本,能够简化设置过程。可以在 Quartz 发行包的 “docs/dbTables”目录中找到创建表的 SQL 脚本。第二,必须定义一些属性

  1. 创建Quartz数据库表

  2. quartz.properties文件中指定JDBCJobStore属性

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

# Using RAMJobStore
## if using RAMJobStore, please be sure that you comment out the following
## - org.quartz.jobStore.tablePrefix,
## - org.quartz.jobStore.driverDelegateClass,
## - org.quartz.jobStore.dataSource
#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

# Using JobStoreTX
## Be sure to run the appropriate script(under docs/dbTables) first to create tables
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

# Configuring JDBCJobStore with the Table Prefix
org.quartz.jobStore.tablePrefix = QRTZ_

# Using DriverDelegate
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate

# Using datasource
org.quartz.jobStore.dataSource = qzDS

# Define the datasource to use
org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = root
org.quartz.dataSource.qzDS.maxConnections = 30
测试

六、集群支持

原理

​ 虽然单个Quartz实例能给予你很好的Job调度能力,但它不能满足典型的企业需求,如可伸缩性、高可靠性满足。假如你需要故障转移的能力并能运行日益增多的 Job,Quartz集群势必成为你应用的一部分了。使用 Quartz 的集群能力可以更好的支持你的业务需求,并且即使是其中一台机器在最糟的时间崩溃了也能确保所有的 Job 得到执行。

​ 一个 Quartz 集群中的每个节点是一个独立的 Quartz 应用,它又管理着其他的节点。意思是你必须对每个节点分别启动或停止。不像许多应用服务器的集群,独立的 Quartz 节点并不与另一其的节点或是管理节点通信。Quartz 应用是通过数据库表来感知到另一应用的。

​ 图:表示了每个节点直接与数据库通信,若离开数据库将对其他节点一无所知

img

搭建步骤
  1. 准备配置文件
  org.quartz.scheduler.instanceName: TestScheduler
  org.quartz.scheduler.instanceId: auto
  
  org.quartz.scheduler.skipUpdateCheck: true
  
  org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
  org.quartz.threadPool.threadCount = 10
  org.quartz.threadPool.threadPriority = 5
  org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
  
  # Using RAMJobStore
  ## if using RAMJobStore, please be sure that you comment out the following
  ## - org.quartz.jobStore.tablePrefix,
  ## - org.quartz.jobStore.driverDelegateClass,
  ## - org.quartz.jobStore.dataSource
  #org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
  
  # Using JobStoreTX
  ## Be sure to run the appropriate script(under docs/dbTables) first to create tables
  org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
  org.quartz.jobStore.isClustered = true
  
  # Configuring JDBCJobStore with the Table Prefix
  org.quartz.jobStore.tablePrefix = QRTZ_
  
  # Using DriverDelegate
  org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  
  # Using datasource
  org.quartz.jobStore.dataSource = qzDS
  
  # Define the datasource to use
  org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
  org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/quartz
  org.quartz.dataSource.qzDS.user = root
  org.quartz.dataSource.qzDS.password = root
  org.quartz.dataSource.qzDS.maxConnections = 30
  1. 修改spring-quartz.xml

    <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="configLocation" value="classpath:quartz.properties" />
        <property name="triggers">
            <list>
                <ref bean="trigger"></ref>
                <ref bean="trigger1"></ref>
            </list>
        </property>
    </bean>
    
  2. 启动spring工厂并测试

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值