Quartz定時任務框架(二) Quartz詳解

z1340954953發表於2018-08-01

目錄

Quartz API

Scheduler排程程式、SchedulerFactory排程程式工廠

scheduler排程程式

SchedulerFactory

Job & JobDetail

JobDataMap

Job例項化的過程

job的註解宣告和併發

job的其他屬性

Trigger觸發器

優先順序Priority


 

Quartz API

  •  Scheduler : 和排程程式互動的主要介面
  • Job : 由排程程式執行的一個用來擴充套件的介面,execute方法裡面執行的是job的具體行為
  • JobDetail : 定義jobs的例項
  • Trigger: 定義排程程式上的job的執行時間,用來設定任務什麼時間觸發
  • JobBuilder: 用來建立或者定義JobDetail例項
  • TriggerBuilder : 用來建立Trigger例項

Scheduler排程程式、SchedulerFactory排程程式工廠

scheduler排程程式

Scheduler維護一個jobDetails和triggers的登錄檔,當時間一到就會觸發job執行

排程程式Scheduler可以通過SchedulerFactory工廠進行建立。一個已經建立的Scheduler可以通過SchedulerFactory獲取。

Scheduler建立後只是出於待機狀態,呼叫scheduler.start()方法啟動,scheduler.shutdown方法關閉,isShutdown檢查狀態。

Scheduler呼叫scheduleJob方法將job加入排程,並在start方法執行後,觸發job執行。

SchedulerFactory

SchedulerFactory有兩個預設的實現類:DirectSchedulerFactoryStdSchedulerFactory

* DirectSchedulerFactory

DirectSchedulerFactory是一個org.quartz.SchedulerFactory的單例實現。 
這裡有一些使用DirectSchedulerFactory的示例程式碼段: 
示例1:你可以使用createVolatileScheduler方法去建立一個不需要寫入資料庫的排程程式例項:

//建立一個擁有10個執行緒的排程程式
DirectSchedulerFactory.getInstance().createVolatileScheduler(10);  
//記得啟用該排程程式
DirectSchedulerFactory.getInstance().getScheduler().start();

 另一種,建立方式:

public void createScheduler(String schedulerName, String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort)

示例程式碼:

// 建立執行緒池
SimpleThreadPool threadPool = new SimpleThreadPool(maxThreads, Thread.NORM_PRIORITY); 
threadPool.initialize(); 

// 建立job儲存器
JobStore jobStore = new RAMJobStore();

//使用所有引數建立排程程式
DirectSchedulerFactory.getInstance().createScheduler("My Quartz Scheduler", "My Instance", threadPool, jobStore, "localhost", 1099); 

// 不要忘了呼叫start()方法來啟動排程程式
DirectSchedulerFactory.getInstance().getScheduler("My Quartz Scheduler", "My Instance").start();

 也可以使用jdbcjobstore

DBConnectionManager.getInstance().addConnectionProvider("someDatasource", new JNDIConnectionProvider("someDatasourceJNDIName"));

JobStoreTX jdbcJobStore = new JobStoreTX(); jdbcJobStore.setDataSource("someDatasource"); jdbcJobStore.setPostgresStyleBlobs(true); jdbcJobStore.setTablePrefix("QRTZ_"); jdbcJobStore.setInstanceId("My Instance");

* StdSchedulerFactory 

StdSchedulerFactory是org.quartz.SchedulerFactory的實現類,它是基於Quartz屬性檔案建立Quartz Scheduler 排程程式的。之前的入門案例使用的就是StdSchedulerFactory,因為我們指定了屬性檔案quartz.properties。

預設情況下是載入當前工作目錄下的”quartz.properties”屬性檔案。如果載入失敗,會去載入org/quartz包下的”quartz.properties”屬性檔案。我們使用JD-GUI反編譯工具開啟quartz.jar,可以在org/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.
#

org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

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

org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

Job & JobDetail

 Job是用來定義任務的執行行為,JobDetail則是用來建立並告知scheduler對應job的各種屬性,JobDetail使用JobBuilder.newJob 建立,建立的時候必須知道job的實現型別。

任務Job有一個名稱name 和組group 來關聯。在一個Scheduler中這二者的組合必須是唯一的。

觸發器任務計劃執行表的執行”機制”。多個觸發器可以指向同一個工作,但一個觸發器只能指向一個工作。

 JobDetail job = JobBuilder.newJob (HelloJob.class).withIdentity("job1", "group1")
                .build();
public class HelloJob implements Job{
	public HelloJob(){}
	@Override
	public void execute(JobExecutionContext paramJobExecutionContext)
			throws JobExecutionException {
		 System.err.println("["+Thread.currentThread().getName()+"]"+"Hello World!  MyJob is executing.");
	}
}

每次scheduler執行job的execute方法都會建立一個新的例項,job執行完將它進行回收處理

當使用預設的SchedularFactory實現時,Job的實現類必須有一個無引數的構造器

Job的實現類中,定義一些狀態變數是沒有任何意義的,因為值不能在任務執行期間保留,因為每次執行一次,都會建立新的job例項,執行完畢就會銷燬。

JobDataMap

JobDataMap是JobDetail實體的一部分,通過它能夠了解Job在執行中的狀態,並且用來儲存一些和job執行過程中可用的資料

JobDataMap是map的一個實現類,提供了一些方法來儲存和檢索原始型別的資料

1. 在JobDatail建立的時候可以將資料放到JobDataMap中,在將job加到任務排程之前

Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .usingJobData("name", "hello world! ")//usingJobData方法將資料放到jobDataMap中
                .usingJobData("age", 110l)
                .usingJobData("flag", false)
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(1)
                        .repeatForever())            
                .build();

2. 在Job的執行期間將資料取出

public class HelloJob implements Job{
	public HelloJob(){}
	@Override
	public void execute(JobExecutionContext context)
			throws JobExecutionException {
		 JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
		 String name = jobDataMap.getString("name");
		 long age = jobDataMap.getLong("age");
		 boolean flag = jobDataMap.getBoolean("flag");
		 System.out.println("name:"+name+",age:"+age+",flag:"+flag);
		 System.err.println("["+Thread.currentThread().getName()+"]"+"Hello World!  MyJob is executing.");
	}
}

儲存進去jobdataMap的資料將會被序列化,可能出現序列化的問題。顯然標準的java型別儲存是安全的,但是如果存放的是序列化自定義的物件,就可能出現序列化問題。

在存放key和字串的模式中,可以將javadatamap作為資料儲存,而不是存放基本型別資料,從而解決消除序列化出現的問題。

另外,Trigger也能和jobdatamap關聯,當使用多個Trigger設定一個job時,對於每個trigger都可以通過jobdataMap提供獨立的輸入。

通過JobExecutionContext能夠獲取javadatamap,是jobdetail和trigger關聯的javadatamap的合併,這這樣獲取的jobdataMap能夠獲取到jobDatail和Trigger觸發器中的值。

 Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .usingJobData("name", "hello world! ")//usingJobData方法將資料放到jobDataMap中
                .usingJobData("age", 110l)
                .usingJobData("flag", false)
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(1)
                        .repeatForever())            
                .build();
public void execute(JobExecutionContext context)
			throws JobExecutionException {
		JobDataMap jobDataMap = context.getMergedJobDataMap();
	}

Job例項化的過程

可以建立一個job類,然後建立多個jobDetail,每個jobDetail都有自己的設定和jobdatamap,最後加到scheduler排程程式中。

舉個例子:你可以建立一個SalesReportJob的job類,作為銷售報表使用,在javadatamap中指定銷售報表的名稱和依據,然後建立這個job的多個jobDetail例項,例如 SalesReportForJoe和SalesReportForMik

重要:當觸發器時間到了,就會載入關聯的JobDetail,依賴於JobFactory的scheduler的配置進行例項化。JobFactory建立新的job例項,並呼叫job中set方法將值設定到jobdatamap的對應key上。jobFactory也可以擴充套件,通過ioc,di完成job例項的構建。

job的註解宣告和併發

@DisallowConcurrentExecution: 新增在job類上,告知quartz在多執行緒環境下不執行job的多個例項。

注意,這個約束是基於jobDetail而不是job類上面的SalesReportJob的job類,定義了多個SalesReportFroJoe和SalesReportForMik,這兩個jobDetail例項,這就意味在多執行緒環境下, 只能執行一個SalesReportForJoe和一個SalesReportForMik定義的job.

@PersistJobDataAfterExecution: 當執行一次execute完畢後,更新jobdatamap中的資料,這樣下次執行execute方法,檢索的是更新的值,而不是原始的值。

如果使用@PersistJobDataAfterExecution註解,強烈建議配合使用@DisallowConcurrentExecution,避免在多執行緒環境下, 多個job導致的資料問題。

job的其他屬性

  • 持久化 - 如果一個任務不是持久化的,則當沒有觸發器關聯它的時候,Quartz會從scheduler中刪除它。
  • 請求恢復 - 如果一個任務請求恢復,一般是該任務執行期間發生了系統崩潰或者其他關閉程式的操作,當服務再次啟動的時候,會再次執行該任務。這種情況下,JobExecutionContext.isRecovering()會返回true

Trigger觸發器

TriggerBuilder 用於建立觸發器Trigger。如果你沒有呼叫withSchedule(..) 方法,會使用預設的schedule 。如果沒有使用withIdentity(..) 會自動生成一個觸發器名稱給你。可以在觸發器使用TriggerBuilder進行例項化的同時設定一些屬性

jobkey: 當觸發器觸發的時候,執行的job

starttime:  觸發器開始觸發的時間

endtime: 觸發器結束觸發的時間,比如每5天觸發一次,endtime是7月1號,那麼最後的執行時間是7月5號。

優先順序Priority

設定屬性優先順序,只有starttime相同才有意義。

當一個任務請求恢復,恢復和原始的優先順序一致。

參考:http://www.quartz-scheduler.org/documentation/quartz-2.1.x/tutorials/tutorial-lesson-04.html

           https://blog.csdn.net/zixiao217/article/details/53053598

 

相關文章