zoukankan      html  css  js  c++  java
  • Quartz学习总结

    一、quartz介绍

    Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,是完全由java开发的一个开源的任务日程管理系统,“任务进度管理器”就是一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统。

    Quartz用一个小Java库发布文件(.jar文件),这个库文件包含了所有Quartz核心功能。这些功能的主要接口(API)是Scheduler接口。它提供了简单的操作,例如:将任务纳入日程或者从日程中取消,开始/停止/暂停日程进度。

    二、核心概念

    Quartz 任务调度的核心元素是 scheduler, trigger 和 job,其中 trigger 和 job 是任务调度的元数据, scheduler 是实际执行调度的控制器。

    1、trigger

    触发器,用于定于任务调度的时间规则,即按照什么时间规则去执行任务。Quartz 中主要提供了四种类型的 trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,和 NthIncludedDayTrigger。Trigger也有一个相关联的JobDataMap,用于给Job传递一些触发相关的参数。
    最常用的:
    SimpleTrigger:用来触发只需执行一次或者在给定时间触发并且重复N次且每次执行延迟一定时间的任务。
    CronTrigger:按照日历触发,例如“每个周五”,每个月10日中午或者10:15分。

    2、job

    被调度的任务,主要有两种类型的 job:无状态的(stateless)和有状态的(stateful)。

    1. Quartz无状态Job实现了org.quartz.Job接口,有状态Job实现了org.quartz.StatefulJob接口。
    2. 对于同一个 trigger 来说,有状态的 job 不能被并行执行,只有上一次触发的任务被执行完之后,才能触发下一次执行。
    3. 一个 job 可以被多个 trigger 关联,但是一个 trigger 只能关联一个 job。

    3、scheduler

    任务调度器,由 scheduler 工厂创建:DirectSchedulerFactory 或者 StdSchedulerFactory。 第二种工厂 StdSchedulerFactory 使用较多,因为 DirectSchedulerFactory 使用起来不够方便,需要作许多详细的手工编码设置。 Scheduler 主要有三种:RemoteMBeanScheduler, RemoteScheduler 和 StdScheduler。

    4、作业管理和存储

    作业一旦被调度,调度器需要记住并且跟踪作业和它们的执行次数。Quartz通过一个称之为作业存储(JobStore)的概念来做作业存储和管理。

    Quartz提供两种基本作业存储类型。第一种类型叫做RAMJobStore,它利用内存来持久化调度程序信息。
    优点: 不要外部数据库,配置容易,运行速度快
    缺点: 因为调度程序信息是存储在被分配给JVM的内存里面,所以,当应用程序停止运行时,所有调度信息将被丢失。另外因为存储到JVM内存里面,所以可以存储多少个Job和Trigger将会受到限制

    第二种类型的作业存储称为JDBC作业存储,提供两种不同的实现。两种JDBC作业存储都需要JDBC驱动程序和后台数据库来持久化调度程序信息。这两种类型的不同在于你是否想要控制数据库事务或者释放控制给应用服务器(这类似于J2EE领域中,容器管理事务CMT和Bean管理事务BMT之间的区别),这两种JDBC作业存储是:
    JobStoreTX:当你想要控制事务或工作在非应用服务器环境中时使用;
    JobStoreCMT:当你工作在应用服务器环境中和想要容器控制事务时使用。
    优点: 支持集群,因为所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务
    缺点: 运行速度的快慢取决与连接数据库的快慢

    三、核心类和关系

    1.核心类

    • QuartzSchedulerThread :负责执行向QuartzScheduler注册的触发Trigger的工作的线程。
    • ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提供运行效率。
    • QuartzSchedulerResources:包含创建QuartzScheduler实例所需的所有资源(JobStore,ThreadPool等)。
    • SchedulerFactory :提供用于获取调度程序实例的客户端可用句柄的机制。
    • JobStore: 通过类实现的接口,这些类要为org.quartz.core.QuartzScheduler的使用提供一个org.quartz.Job和org.quartz.Trigger存储机制。作业和触发器的存储应该以其名称和组的组合为唯一性。
    • QuartzScheduler :这是Quartz的核心,它是org.quartz.Scheduler接口的间接实现,包含调度org.quartz.Jobs,注册org.quartz.JobListener实例等的方法。
    • Scheduler :这是Quartz Scheduler的主要接口,代表一个独立运行容器。调度程序维护JobDetails和触发器的注册表。 一旦注册,调度程序负责执行作业,当他们的相关联的触发器触发(当他们的预定时间到达时)。
    • Trigger :具有所有触发器通用属性的基本接口,描述了job执行的时间出发规则。 - 使用TriggerBuilder实例化实际触发器。
    • JobDetail :传递给定作业实例的详细信息属性。 JobDetails将使用JobBuilder创建/定义。
    • Job:要由表示要执行的“作业”的类实现的接口。只有一个方法 void execute(jobExecutionContext context)
      (jobExecutionContext 提供调度上下文各种信息,运行时数据保存在jobDataMap中)
      Job有个子接口StatefulJob ,代表有状态任务。
      有状态任务不可并发,前次任务没有执行完,后面任务处于阻塞等到。

    2.关系:

    四、quartz使用

    1.依赖

    <dependency>
    
        <groupId>org.quartz-scheduler</groupId>
    
        <artifactId>quartz</artifactId>
        
    <version>2.2.3</version>
     
    </dependency>
    <dependency>
    
        <groupId>org.quartz-scheduler</groupId>
    
        <artifactId>quartz-jobs</artifactId>
    
        <version>2.2.3</version>
     
    </dependency> 
    

    2.原生应用

    定义job

    public class MyJob implements Job {
        private static Logger logger = LoggerFactory.getLogger(MyJob.class);
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            //打印当前的执行时间 例如 2019-11-12 00:00:00
            Date date = new Date();
            SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println("现在的时间是:"+ sf.format(date));
            //具体的业务逻辑
            System.out.println("开始生成任务报表 或 开始发送邮件");
            logger.info("开始执行定时任务");
        }
    }
    

    创建trigger,schedule,并启动定时任务

    //创建一个jobDetail的实例,将该实例与HelloJob Class绑定
    JobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("myJob").build();
    //创建一个Trigger触发器的实例,定义该job立即执行,并且每2秒执行一次,一直执行
    SimpleTrigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();
    //创建schedule实例
    StdSchedulerFactory factory = new StdSchedulerFactory();
    Scheduler scheduler = factory.getScheduler();
    scheduler.start();
    scheduler.scheduleJob(jobDetail,trigger);
    

    3.Quartz+Spring集成使用

    先使用quartz自带的建表语句创建表.
    配置quartz.properties,如下:

    # Default Properties file for use by StdSchedulerFactory
    # to create a Quartz Scheduler Instance, if a different
    # properties file is not explicitly specified.
    #
    # 调度器实例的名字,使用默认的DefaultQuartzScheduler就好
    org.quartz.scheduler.instanceName: DefaultQuartzScheduler
    # 调度器实例的ID, 选择AUTO
    org.quartz.scheduler.instanceId:AUTO
    org.quartz.scheduler.rmi.export: false
    org.quartz.scheduler.rmi.proxy: false
    # 跳过更新检查
    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
    #
    # quartz默认的是将job信息存储在内存中,quartz集群必须将job信息持久化到数据库中
    org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
    org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
    org.quartz.jobStore.misfireThreshold:60000
    ############
    org.quartz.jobStore.useProperties:true
    #quartz数据表的前缀,quartz的数据表在 quartz-2.2.3docsdbTables 文件夹中,
    #选择对应的数据库版本,将数据库创建出来
    org.quartz.jobStore.tablePrefix:QRTZ_
    # 最关键的  是否支持集群 选择true
    org.quartz.jobStore.isClustered:true
    org.quartz.jobStore.clusterCheckinInterval:15000
    # dataSource
    #org.quartz.jobStore.dataSource:myDS
    #org.quartz.dataSource.myDS.connectionProvider.class:com.abc.util.MyPoolingconnectionProvider
    #org.quartz.dataSource.myDS.driver: com.mysql.jdbc.Driver 
    #org.quartz.dataSource.myDS.url: jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8 
    #org.quartz.dataSource.myDS.user: root 
    #org.quartz.dataSource.myDS.password: root 
    #org.quartz.dataSource.myDS.maxConnections: 10
    

    db.properties:

    #quartz配置对应数据库
    jdbc.quartz.driver=com.mysql.jdbc.Driver
    jdbc.quartz.url=jdbc:mysql://127.0.0.1:26500/quartz?useUnicode=true&characterEncoding=utf-8
    jdbc.quartz.username=root
    jdbc.quartz.password=vislecaina
    

    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-4.0.xsd
                            ">
    
        <!-- 任务 -->
        <bean id="jobDetailFactoryBeanExample" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
            <property name="jobClass" value="com.fiberhome.quartz.task.job.MyJob"/>
            <!-- 执行完成仍保留在数据库中 -->
            <property name="durability" value="true"/>
            <!-- 这里设置的jobDataAsMap可以传递一些参数给作业任务 -->
            <property name="jobDataAsMap">
                <map>
                    <entry key="wish" value="hello"/>
                </map>
            </property>
        </bean>
    
        <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
            <!-- 这里的JobDetail指的就是我们配置的作业任务的bean -->
            <property name="jobDetail" ref="jobDetailFactoryBeanExample" />
            <!-- 延迟5秒开始 -->
            <property name="startDelay" value="5000"></property>
            <!-- 每3秒重复一次 -->
            <property name="repeatInterval" value="60000"></property>
        </bean>
    
        <bean id="startQuartz" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">
            <property name="configLocation" value="classpath:quartz.properties" />
            <property name="dataSource" ref="quartzDataSource" />
            <!-- 如果这个覆盖配置为false,quratz启动以后将以数据库的数据为准,配置文件的修改不起作用。-->
            <property name="overwriteExistingJobs" value="true" />
            <!--指定使用哪些触发器,spring会去调度触发相应的触发器,进而对作业任务进行调度处理-->
            <property name="triggers">
                <list>
                    <ref bean="simpleTrigger"/>
                </list>
            </property>
            <property name="jobFactory">
                <bean class="com.fiberhome.quartz.task.jobfactory.JobFactory"/>
            </property>
            <property name="startupDelay" value="1"/>
        </bean>
    
    </beans>
    

    Job类:

    public class MyJob implements Job {
    
        @Autowired
        private UserService userService;
        /**
         * 要实现的任务
         *
         * @param context job上下文
         * @throws JobExecutionException
         */
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            User user = userService.getUser(1001);
            System.out.println("执行定时任务...");
            System.out.println(user);
        }
    }
    

    JobFactory类:

    public class JobFactory extends SpringBeanJobFactory {
    
        @Autowired
        private AutowireCapableBeanFactory autowireCapableBeanFactory;
    
        @Override
        protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
            Object jobInstance = super.createJobInstance(bundle);
            autowireCapableBeanFactory.autowireBean(jobInstance);
            return jobInstance;
        }
    }
  • 相关阅读:
    Effective Java 19 Use interfaces only to define types
    Effective Java 18 Prefer interfaces to abstract classes
    Effective Java 17 Design and document for inheritance or else prohibit it
    Effective Java 16 Favor composition over inheritance
    Effective Java 15 Minimize mutability
    Effective Java 14 In public classes, use accessor methods, not public fields
    Effective Java 13 Minimize the accessibility of classes and members
    Effective Java 12 Consider implementing Comparable
    sencha touch SortableList 的使用
    sencha touch dataview 中添加 button 等复杂布局并添加监听事件
  • 原文地址:https://www.cnblogs.com/leton/p/11896001.html
Copyright © 2011-2022 走看看