深入解读Quartz任务调度器

深入解读Quartz任务调度器

1.Quartz简介

1.1.概要

Quartz是OpenSymphony提供的强大的开源任务调度框架。  
  官网:http://www.quartz-scheduler.org
  纯Java实现,精细控制排程。

image

1.2.Quartz特点

  1. 强大的调度能力
  2. 灵活的应用方式
  3. 强大的分布式和集群能力

1.3.Quartz设计模式

  • Builder模式
  • 组件模式
  • Factory模式
  • 链式写法

1.4.Quartz体系结构

1.4.1.三大核心

  • 调度器
  • 任务
  • 触发器
image

1.4.2.重要组成

1)任务:

  • Job:表示一个工作,要执行的具体内容。此接口中只有一个方法。要创建一个任务,必须得实现这个接口。该接口只有一个execute方法,任务每次被调用的时候都会执行这个execute方法的逻辑,类似TimerTask的run方法,在里面编写业务逻辑。

      public class TestJob implements Job {
          /**把要执行的操作,写在execute方法中  */
          @Override
          public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
              SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
              System.out.println("I can do something...");
              System.out.println(sdf.format(new Date()));
          }
      }
    

生命周期:在每次调度器执行job时,它在调用execute方法前会创建一个新的job实例,当调用完成之后,关联的job对象实例会被释放,释放的实例会被垃圾回收机制回收。

  • JobBuilder:可向任务传递数据,通常情况下,我们使用它就可向任务类发送数据了,如有特别复杂的传递参数,它提供了一个传递递:JobDataMap对象的方法

      JobDetail jobDetail =  JobBuilder.newJob(TestJob.class).withIdentity("testJob","group1").build();
    
  • JobDetail:用来保存我们任务的详细信息。一个JobDetail可以有多个Trigger,但是一个Trigger只能对应一个JobDetail。下面是JobDetail的一些常用的属性和含义:

image
  • JobStore:负责跟踪所有你给scheduler的“工作数据”:jobs, triggers, calendars, 等。
    • RAMJobStore:是使用最简单的也是最高效(依据CPU时间)的JobStore 。RAMJobStore 正如它名字描述的一样,它保存数据在RAM。缺点是你的应用结束之后所有的数据也丢失了--这意味着RAMJobStore 不具有保持job和trigger持久的能力。对于一些程序是可以接受的,甚至是期望的,但对于其他的程序可能是灾难性的。使用RAMJobStore配置Quartz:配置如下

        org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore 
      
    • JDBCJobStore:以JDBC的方式保存数据在数据库中。它比RAMJobStore的配置复杂一点,也没有RAMJobStore快。然而,性能缺点不是糟透了,特别是如果你在数据库表主键上建立了索引。在机器之间的LAN(在scheduler 和数据库之间)合理的情况下,检索和更新一个被触发的Trigger花费的时间少于10毫秒。几乎适用于所有的数据库,广泛用于 Oracle。PostgreSQL, MySQL, MS SQLServer, HSQLDB, 和DB2。使用JDBCJobStore之前你必须首先创建一系列Quartz要使用的表。你可以发现表创建语句在Quartz发布目录的 “docs/dbTables”下面。你需要确定你的应用要使用的事务类型。如果你不想绑定调度命令(例如增加和移除Trigger)到其他的事务,你可以使用JobStoreTX (最常用的选择)作为你的Jobstore。如果你需要Quartz和其他的事务(例如在J2EE应用服务器中)一起工作,你应该使用JobStoreCMT ,Quartz 将让应用服务器容器管理这个事务。使用JobStoreTx配置Quartz:

        org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX  
        org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate  
        #配置表的前缀  
        org.quartz.jobStore.tablePrefix = QRTZ_  
        #使用JNDI数据源的时候,数据源的名字  
        org.quartz.jobStore.dataSource = myDS   
      
    • TerracottaJobStore:提供了一个方法:在不使用数据库的情况下使它具有收缩性和强壮性。可以是集群的也可以是非集群的,在这两种情况下为你的job数据提供了一个存储机制用于应用程序重启之间持久,因为数据是存储在Terracotta服务器。它的性能比使用数据库访问JDBCJobStore好一点儿(大约是一个数量级),但是明显比RAMJobStore慢。使用TerracottaJobStore配置Quartz:

        org.quartz.jobStore.class = org.terracotta.quartz.TerracottaJobStore  
        org.quartz.jobStore.tcConfigUrl = localhost:9510  
      
  • JobDataMap:中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法。
    • 存:

        JobDetail jobDetail =  JobBuilder.newJob(TestJob.class).withIdentity("testJob","group1").usingJobData("date1","存内容").build(); 
      
    • 取:

        public class TestJob implements Job {
            /**把要执行的操作,写在execute方法中  */
            @Override
            public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
                JobKey key = jobExecutionContext.getJobDetail().getKey();
                JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
                String date1 = jobDataMap.getString("date1");
            }
        }
      

2)触发器:用来触发执行Job

image

2.1)触发器通用属性:

  • Jobkey:表示job实例的标识,触发器被触发时,该指定的job实例会被执行
  • StartTime:表示触发器的时间表首次被触发的时间,它的值类型为:java.util.Date
  • EndTime:指定触发器的不再被触发的时间,它的值类型为:java.util.Date

2.2)触发器类型:

  • SimpleTrigger: 主要是针对一些相对简单的时间触发进行配置使用,比如在指定的时间开始然后在一定的时间间隔之内重复执行一个Job,同时可以任意指定重复的次数。下面就是使用一个SimpleTrigger的例子:

      //创建触发器 每3秒钟执行一次(无开始时间和结束时间)
      Trigger trigger = TriggerBuilder.newTrigger()
                  .withIdentity("trigger1", "group3")
                  .withSchedule(
                  SimpleScheduleBuilder.simpleSchedule()
                  .withIntervalInSeconds(3).repeatForever()).build();
      //创建触发器 每3秒钟执行一次(有开始时间和结束时间)
      long now = new Date().getTime();
      Date start = new Date(now+6000);
      Date end = new Date(now+12000);
      //创建触发器 每3秒钟执行一次
      Trigger trigger = TriggerBuilder.newTrigger()
                        .withIdentity("trigger1", "group3")
                        .startAt(start)
                        .endAt(end)
                        .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever()).build();
    

SimpleTrigger具有丰富的构造函数,根据业务需求构造不同的构造函数。

  • CronTrigger: 可以配置更复杂的触发时刻表,基于日历的作业触发器,而不像SimpleTrigger那样精确指定间隔时间,比SimpleTrigger更加常用。

Cron表达式:用于配置CronTrigger实例,是由7个表达式组成的字符串,描述了时间表的详细信息。
  格式为:[秒][分][时][日][月][周][年]
  Cron表达式特殊字符意义对应表:
  

image

  通配符说明:
  
image

  Cron表达式例子:
  
image

    TriggerBuilder.newTrigger().withIdentity("trigger2","group2")
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 ? * 6L *")).build();

Cron表达式小技巧:
  1. ‘L’和‘W’可以一起组合使用
  2. 周字段英文字母不区分大小写即MOM与mom相同
  3. 利用工具,在线生成cron表达式:http://cron.qqe2.com/

  • NthIncludedDayTrigger:是 Quartz 开发团队最新加入到框架中的一个 Trigger。它设计用于在每一间隔类型的第几天执行 Job。例如,你要在每个月的 15 号执行开票的 Job,用 NthIncludedDayTrigger就再合适不过了。

      NthIncludedDayTrigger trigger = new NthIncludedDayTrigger("NthIncludedDayTrigger",Scheduler.DEFAULT_GROUP);
                  trigger.setN(15);
                  trigger.setIntervalType(NthIncludedDayTrigger.INTERVAL_TYPE_MONTHLY);
    

3)调度器Scheduler
  代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中Trigger和JobDetail。
  Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。
  可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。可以通过Scheduler# getContext()获取对应的SchedulerContext实例;

    SchedulerFactory schedulerfactory=new StdSchedulerFactory();
    Scheduler scheduler = schedulerfactory.getScheduler();

    DirectSchedulerFactory factory = DirectSchedulerFactory.getInstance();
    try {
        Scheduler scheduler = factory.getScheduler();
    } catch (SchedulerException e) {
        e.printStackTrace();
    }

4)SchedulerFactory:

  • 使用一组参数(java.util.Properties)来创建和出书啊Quartz调度器
  • 配置参数一般存储在quartz.properties中
  • 调用getScheduler方法就能创建和初始化调度器

5)quartz.properties:
Quartz-Job的quartz.properties配置文件说明,此文件在quartz的jar包有,可直接拿过来使用不过只有基本的几个配置 自己可根据需要进行扩充;另外如果项目中没有对该配置文件重写,则Quartz会加载自己jar包中的quartz.properties文件。

    # Default Properties file for use by StdSchedulerFactory  
    # to create a Quartz Scheduler Instance, if a different  
    # properties file is not explicitly specified.  
    #  
    # ===========================================================================  
    # Configure Main Scheduler Properties 调度器属性  
    # ===========================================================================  
    org.quartz.scheduler.instanceName: DefaultQuartzScheduler  
    #org.quartz.scheduler.instanceid:AUTO  
    org.quartz.scheduler.rmi.export: false  
    org.quartz.scheduler.rmi.proxy: false  
    org.quartz.scheduler.wrapJobExecutionInUserTransaction: false  
    # ===========================================================================    
    # Configure ThreadPool 线程池属性    
    # ===========================================================================  
    #线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)  
    org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool  
    #指定线程数,至少为1(无默认值)(一般设置为1-100直接的整数合适)  
    org.quartz.threadPool.threadCount: 10  
    #设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)  
    org.quartz.threadPool.threadPriority: 5  
    #设置SimpleThreadPool的一些属性  
    #设置是否为守护线程  
    #org.quartz.threadpool.makethreadsdaemons = false  
    #org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true  
    #org.quartz.threadpool.threadsinheritgroupofinitializingthread=false  
    #线程前缀默认值是:[Scheduler Name]_Worker  
    #org.quartz.threadpool.threadnameprefix=swhJobThead;  
    # 配置全局监听(TriggerListener,JobListener) 则应用程序可以接收和执行 预定的事件通知  
    # ===========================================================================  
    # Configuring a Global TriggerListener 配置全局的Trigger监听器  
    # MyTriggerListenerClass 类必须有一个无参数的构造函数,和 属性的set方法,目前2.2.x只支持原始数据类型的值(包括字符串)  
    # ===========================================================================  
    #org.quartz.triggerListener.NAME.class = com.swh.MyTriggerListenerClass  
    #org.quartz.triggerListener.NAME.propName = propValue  
    #org.quartz.triggerListener.NAME.prop2Name = prop2Value  
    # ===========================================================================  
    # Configuring a Global JobListener 配置全局的Job监听器  
    # MyJobListenerClass 类必须有一个无参数的构造函数,和 属性的set方法,目前2.2.x只支持原始数据类型的值(包括字符串)  
    # ===========================================================================  
    #org.quartz.jobListener.NAME.class = com.swh.MyJobListenerClass  
    #org.quartz.jobListener.NAME.propName = propValue  
    #org.quartz.jobListener.NAME.prop2Name = prop2Value  
    # ===========================================================================    
    # Configure JobStore 存储调度信息(工作,触发器和日历等)  
    # ===========================================================================  
    # 信息保存时间 默认值60秒  
    org.quartz.jobStore.misfireThreshold: 60000  
    #保存job和Trigger的状态信息到内存中的类  
    org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore  
    # ===========================================================================    
    # Configure SchedulerPlugins 插件属性 配置  
    # ===========================================================================  
    # 自定义插件    
    #org.quartz.plugin.NAME.class = com.swh.MyPluginClass  
    #org.quartz.plugin.NAME.propName = propValue  
    #org.quartz.plugin.NAME.prop2Name = prop2Value  
    #配置trigger执行历史日志(可以看到类的文档和参数列表)  
    org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingTriggerHistoryPlugin    
    org.quartz.plugin.triggHistory.triggerFiredMessage = Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}    
    org.quartz.plugin.triggHistory.triggerCompleteMessage = Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction code: {9}    
    #配置job调度插件  quartz_jobs(jobs and triggers内容)的XML文档    
    #加载 Job 和 Trigger 信息的类   (1.8之前用:org.quartz.plugins.xml.JobInitializationPlugin)  
    org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin  
    #指定存放调度器(Job 和 Trigger)信息的xml文件,默认是classpath下quartz_jobs.xml  
    org.quartz.plugin.jobInitializer.fileNames = my_quartz_job2.xml    
    #org.quartz.plugin.jobInitializer.overWriteExistingJobs = false    
    org.quartz.plugin.jobInitializer.failOnFileNotFound = true    
    #自动扫描任务单并发现改动的时间间隔,单位为秒  
    org.quartz.plugin.jobInitializer.scanInterval = 10  
    #覆盖任务调度器中同名的jobDetail,避免只修改了CronExpression所造成的不能重新生效情况  
    org.quartz.plugin.jobInitializer.wrapInUserTransaction = false  
    # ===========================================================================    
    # Sample configuration of ShutdownHookPlugin  ShutdownHookPlugin插件的配置样例  
    # ===========================================================================  
    #org.quartz.plugin.shutdownhook.class = \org.quartz.plugins.management.ShutdownHookPlugin  
    #org.quartz.plugin.shutdownhook.cleanShutdown = true  
    #  
    # Configure RMI Settings 远程服务调用配置  
    #  
    #如果你想quartz-scheduler出口本身通过RMI作为服务器,然后设置“出口”标志true(默认值为false)。  
    #org.quartz.scheduler.rmi.export = false  
    #主机上rmi注册表(默认值localhost)  
    #org.quartz.scheduler.rmi.registryhost = localhost  
    #注册监听端口号(默认值1099)  
    #org.quartz.scheduler.rmi.registryport = 1099  
    #创建rmi注册,false/never:如果你已经有一个在运行或不想进行创建注册  
    # true/as_needed:第一次尝试使用现有的注册,然后再回来进行创建  
    # always:先进行创建一个注册,然后再使用回来使用注册  
    #org.quartz.scheduler.rmi.createregistry = never  
    #Quartz Scheduler服务端端口,默认是随机分配RMI注册表  
    #org.quartz.scheduler.rmi.serverport = 1098  
    #true:链接远程服务调度(客户端),这个也要指定registryhost和registryport,默认为false  
    # 如果export和proxy同时指定为true,则export的设置将被忽略  
    #org.quartz.scheduler.rmi.proxy = false  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容