深入解讀Quartz任務排程器

CatalpaFlat發表於2017-11-16

深入解讀Quartz任務排程器

1.Quartz簡介

1.1.概要

  Quartz是OpenSymphony提供的強大的開源任務排程框架。  
  官網:www.quartz-scheduler.org
  純Java實現,精細控制排程。

1.2.Quartz特點

  1. 強大的排程能力
  2. 靈活的應用方式
  3. 強大的分散式和叢集能力

    1.3.Quartz設計模式

  • Builder模式
  • 元件模式
  • Factory模式
  • 鏈式寫法

    1.4.Quartz體系結構

    1.4.1.三大核心

  • 排程器
  • 任務
  • 觸發器

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的一些常用的屬性和含義:  

  

  • 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

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表示式特殊字元意義對應表:
  


  萬用字元說明:
  

  Cron表示式例子:
  

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

  Cron表示式小技巧:
  1. ‘L’和‘W’可以一起組合使用
  2. 周欄位英文字母不區分大小寫即MOM與mom相同
  3. 利用工具,線上生成cron表示式: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  複製程式碼

相關文章