天天看點

Quartz 基本概念

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/catoop/article/details/70246683

核心概念

1.Job:Job是任務執行的流程,是一個類

2.JobDetail:JobDetail是Job是執行個體,是一個對象,包含了該執行個體的執行計劃和所需要的資料

3.Trigger:Trigger是定時器,決定任務何時執行

4.Scheduler:排程器,排程器接受一組JobDetail+Trigger即可安排一個任務,其中一個JobDetail可以關聯多個Trigger

執行個體

1.初始化:

  1. Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

  2. scheduler.start();

當程式退出時,應該主動調用shutdown()方法關閉該排程器。

2.Job:

一個Job類需要實作org.quartz.Job接口,這個接口值需要實作一個方法即可:

  1. void execute(JobExecutionContext context) throws JobExecutionException

context是重要的上下文,可以通路到關聯的JobDetail對象和本次觸發的Trigger對象,以及在此之上設定的資料。

3.JobDetail:

可以使用JobBuilder來建構一個JobDetail對象:

  1. JobDetail job = JobBuilder.newJob(MyJob.class) // MyJob是我實作的Job類

  2. .withIdentity("myjob") // 可以給該JobDetail起一個id,便于之後的檢索

    。也可以 .withIdentity("myjob", "group1")
  3. .requestRecovery() // 執行中應用發生故障,需要重新執行

  4. .storeDurably() // 即使沒有Trigger關聯時,也不需要删除該JobDetail

  5. .usingJobData("key1", "value1")

  6. .usingJobData("key2", "value2") // 以Key-Value形式關聯資料

  7. .build();

Quartz因為考慮到有些任務不是幂等的,不可以多次重複執行,是以預設沒有開啟“requestRecovery”。當确認業務中允許一次任務執行兩次的情況下,可以開啟該選項,則任務肯定不會因為應用停止而漏調用,但缺點就是,有可能會重複調用。

每個JobDetail内都有一個Map,包含了關聯到這個Job的資料,在Job類中,可以通過context取出該資料,進行業務流程處理。

4.Trigger:

可以使用TriggerBuilder來建構一個Trigger對象:

  1. Trigger trigger = TriggerBuilder.newTrigger()

  2. .forJob("myjob") // 關聯上述的JobDetail

  3. .withIdentity("myjob-trigger1") // 給該Trigger起一個id

  4. .startAt(DateBuilder.futureDate(20, IntervalUnit.SECOND)) // 延遲20秒開始

  5. .withSchedule(SimpleScheduleBuilder.repeatMinutelyForever()) // 每分鐘觸發一次,無限循環

  6. .usingJobData("key3", "value3")

  7. .usingJobData("key4", "value4") // 以Key-Value形式關聯資料

  8. .build();

5.設定:

因為上述的Trigger已經關聯了JobDetail,可以使用

  1. scheduler.scheduleJob(trigger);

把這一組JobDetail和Trigger加載到排程器上,接下來就會按照計劃執行Job任務。

6.配置檔案:

配置檔案不是必須的,Quartz對配置項都是有預設值的,當需要自定義的時候,可以在classpath路徑下放一個quartz.properties檔案,Quartz的StdSchedulerFactory在啟動時會自動加載該配置檔案。

比較值得關注的是這兩個配置項:

  1. org.quartz.threadPool.threadCount=50

  2. org.quartz.scheduler.batchTriggerAcquisitionMaxCount=50

第一個配置項是線程池裡的線程數,預設值是10,當執行任務會并發執行多個耗時任務時,要根據業務特點選擇線程池的大小。

第二個配置是,當檢查某個Trigger應該觸發時,預設每次隻Acquire一個Trigger,(為什麼要有Acquire的過程呢?是為了防止多線程通路的情況下,同一個Trigger被不同的線程多次觸發)。尤其是使用JDBC JobStore時,一次Acquire就是一個update語句,盡可能一次性的多擷取幾個Trigger,一起觸發,當定時器數量非常大的時候,這是個非常有效的優化。當定時器數量比較少時,觸發不是極為頻繁時,這個優化的意義就不大了。

6.持久化

如果定時器在業務中屬于關鍵資料,需要在故障重新開機後恢複狀态,則需要把Quartz配置為持久化模式。預設情況下,所有定時任務和資料都儲存在記憶體中,在應用重新開機後狀态會消失。

JobStore決定了Quartz如何存儲任務和觸發器,預設值是org.quartz.simpl.RAMJobStore,我們需要把它配置為org.quartz.impl.jdbcjobstore.JobStoreTX,即可以使用JDBC資料源,把狀态持久化到關系型資料庫中。

用H2資料庫進行舉例,配置檔案如下:

  1. org.quartz.dataSource.DATA_SOURCE_NAME.driver=org.h2.Driver

  2. org.quartz.dataSource.DATA_SOURCE_NAME.URL=jdbc:h2:quartz;MVCC=TRUE

  3. org.quartz.dataSource.DATA_SOURCE_NAME.user=sa

  4. org.quartz.dataSource.DATA_SOURCE_NAME.password=

  5. org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX

  6. org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate

  7. org.quartz.jobStore.dataSource=DATA_SOURCE_NAME

  8. # org.quartz.jobStore.useDBLocks=true

Quartz為了保證多線程下的一緻性,使用了鎖,但是使用了使用Lock表裡的一行記錄來模拟鎖的形式來實作的,其實性能很糟糕,還不如使用預設的基于Java的Monitor鎖的效果好,是以我注釋掉了這個配置項。

之後,打開quartz-x.x.x.tar.gz包内的docs/dbTables檔案夾内有各種資料庫下的建表語句,直接在資料庫中執行,把表先建好。

配置檔案中增加上述對JobStore的設定後,代碼不用修改一行,所有的任務和觸發器已經是持久化的了,當應用停機重新開機後,錯過的(misfire)任務和上次正在執行的(recovery)任務都會回複狀态,如果JobDetail指定了requestRecovery,上次執行中,但沒有執行完畢的任務會重新執行一遍。

注意事項:

> 一個任務JOB可以添加多個Trigger 但是一個Trigger隻能綁定一個JOB 這點需要注意。

> 在用JobBuilder建立JobDetail的時候,有一個storeDurably()方法,可以在沒有觸發器指向任務的時候,使用 sched.addJob(job, true) 将任務儲存在隊列中了。而後使用 sched.scheduleJob 觸發。如果不使用 storeDurably ,則在添加 Job 到引擎的時候會抛異常,意思就是該 Job 沒有對應的 Trigger。

> 要求cluster上所有的node的時間應該是一樣的。