穿戴式跳d放在里面逛超市的_av无码网站在线_国产午夜亚洲欧美家园美丽_久久无码一二三四_精品久久香蕉国产线看观看亚洲

Top
首頁(yè)>即刻 > 正文

天天視訊!【定時(shí)調(diào)度】- 01 quartz的基礎(chǔ)你真的了解嗎

發(fā)布時(shí)間:2023-03-24 07:16:05        來源:騰訊云

概述

Quartz一款功能豐富、歷史悠久,完全基于Java實(shí)現(xiàn)的開源任務(wù)調(diào)度框架,Java調(diào)度領(lǐng)域知名度非常高。其簡(jiǎn)單易用、穩(wěn)定可靠的特性,使其被很多第三方應(yīng)用將其當(dāng)成調(diào)度框架基礎(chǔ)依賴,如spring boot已內(nèi)置集成quartz,elastic-job調(diào)度框架則將quartz作為其底層基礎(chǔ)實(shí)現(xiàn)進(jìn)行封裝,xxl-job曾經(jīng)歷史版本也是集成quartz作為其觸發(fā)實(shí)現(xiàn)機(jī)制基礎(chǔ),不過在最新版本采用時(shí)間輪實(shí)現(xiàn)已將quartz移除。

核心三叉戟

使用quartz api時(shí),最核心三件套如下:

Scheduler

SchedulerFactoryScheduler從名稱就很容易識(shí)別這里采用工廠設(shè)計(jì)模式,Schedulerquartz暴露出來供開發(fā)使用的一個(gè)最重要組件,從開發(fā)者視角來看它就是quartz的門面,對(duì)quartz的各種操作都是通過Scheduler進(jìn)行串聯(lián),類似于quartz的大管家、代言人角色。


【資料圖】

“這種設(shè)計(jì)模式在開源框架中很常見,比如mybatisSqlSessionFactorySqlSession,通過給開發(fā)者提供大管家組件,通過一個(gè)組件串聯(lián)起所有核心功能,簡(jiǎn)化了開發(fā)人員上手框架難度。

一般一個(gè)應(yīng)用只會(huì)對(duì)應(yīng)一個(gè)Scheduler實(shí)例,不同Scheduler實(shí)例之間通過schedulerName進(jìn)行隔離,所有的quartz數(shù)據(jù)庫(kù)表設(shè)計(jì)中都有sched_name這一列字段,這樣Scheduler處理任務(wù)時(shí)只會(huì)操作數(shù)據(jù)庫(kù)表中對(duì)應(yīng)schedulerName下的數(shù)據(jù)。quartz集群就是利用多個(gè)Scheduler實(shí)例配置相同schedulerName名稱,實(shí)現(xiàn)多機(jī)器同時(shí)處理同一個(gè)schedulerName下任務(wù)來達(dá)到集群效果。

schedulerName可以通過org.quartz.scheduler.instanceName進(jìn)行配置,默認(rèn)名稱為QuartzScheduler。

Scheduler操作的主要是JobDetailTrigger兩個(gè)組件,JobDetail封裝的是任務(wù)配置信息,而Trigger觸發(fā)器封裝了任務(wù)觸發(fā)信息,它們是1:N關(guān)系,即一個(gè)JobDetail可以關(guān)聯(lián)多個(gè)Trigger觸發(fā)器,但是一個(gè)Trigger觸發(fā)器只能綁定到一個(gè)Job上。

JobDetail

JobDetail組件封裝了quartz調(diào)度任務(wù)定義信息,下面是JobDetail組件常規(guī)使用方式如下:

// JobDataMap實(shí)現(xiàn)Map接口,任務(wù)調(diào)度時(shí)存儲(chǔ)到JobExecuteContext中,可以傳遞給Job實(shí)例JobDataMap jobDataMap = new JobDataMap();jobDataMap.put("name", "zhangsan");jobDataMap.put("time", System.currentTimeMillis());JobDetail jobDetail = JobBuilder      // 綁定任務(wù)類            .newJob(QuartzCronJob.class)            .storeDurably()      // job對(duì)應(yīng)ID            .withIdentity("job2", "DEFAULT")             .usingJobData(jobDataMap)            .build();JobKey jobKey = jobDetail.getKey();if (scheduler.checkExists(jobKey)) { log.warn("調(diào)度任務(wù)已存在,刪除后重新添加:{}", jobKey); scheduler.interrupt(jobKey);//停止JOB    /**      * deleteJob操作在刪除Job之前,會(huì)執(zhí)行unscheduleJob()取消job和trigger關(guān)聯(lián)    */    scheduler.deleteJob(jobKey);}// 將JobDetail任務(wù)定義信息插入quartz表scheduler.addJob(jobDetail, true);

JobDetail操作比較簡(jiǎn)單,主要有兩點(diǎn)需要注意:1、newJob(Class jobClass)操作綁定任務(wù)類,任務(wù)類就是封裝用戶業(yè)務(wù)邏輯類;2、withIdentity(String name, String group)給該任務(wù)設(shè)置一個(gè)身份ID,后續(xù)可以通過該身份ID進(jìn)行管理,為方便靈活管理quartz抽象出group概念,這樣可以批量對(duì)一組作業(yè)進(jìn)行批量操作,身份ID使用JobKey進(jìn)行封裝。

使用ScheduleraddJob(JobDetail jobDetail, boolean replace)方法就將創(chuàng)建的Job定義信息添加到quartz中,一般采用數(shù)據(jù)庫(kù)持久化模式,即這里就會(huì)將Job定義信息插入到qrtz_job_details表中(見下圖)。

下面來看下幾個(gè)關(guān)鍵字段:

sched_name:上面說過,用來關(guān)聯(lián)對(duì)應(yīng)的Scheduler實(shí)例is_durable:是否持久化is_nonconcurrent:是否允許同一個(gè)作業(yè)可以同時(shí)多個(gè)實(shí)例執(zhí)行,比如一個(gè)任務(wù)間隔1秒,但其執(zhí)行時(shí)間為2秒,通過該屬性控制是否允許同一個(gè)作業(yè)有多個(gè)任務(wù)同時(shí)允許,參見@DisallowConcurrentExecutionis_update_data: 任務(wù)已經(jīng)執(zhí)行中,是否允許更新JobDataMap持久化信息,參見@PersistJobDataAfterExecutionrequests_recovery: 故障恢復(fù)使用,具體參見后續(xù)源碼分析job_data:JobDataMap序列化后存儲(chǔ)到字段中

Trigger

任務(wù)定義完成,但是任務(wù)按照怎么周期性規(guī)則進(jìn)行觸發(fā)執(zhí)行,這就要看Trigger觸發(fā)器的臉色了

Trigger組件常規(guī)使用方式如下:

JobDataMap jobDataMap = new JobDataMap();jobDataMap.put("name", "lisi");jobDataMap.put("address", "China");Trigger trigger = TriggerBuilder            .newTrigger()            .withIdentity("trigger1", "DEFAULT")            .usingJobData(jobDataMap)            .startAt(new Date())            .endAt(new Date(System.currentTimeMillis()+38 * 60 * 1000))            .withSchedule(CronScheduleBuilder.cronSchedule("*/10 * * * * ?"))            .forJob(new JobKey("job1", "DEFAULT"))            .build();//時(shí)間TriggerKey triggerKey = trigger.getKey();if(scheduler.checkExists(triggerKey)){ scheduler.unscheduleJob(triggerKey);}//必須綁定jobscheduler.scheduleJob(trigger);

JobDetail類似,主要有兩點(diǎn)需要注意:1、同withIdentity(String name, String group),同理給該觸發(fā)器設(shè)置一個(gè)身份ID,對(duì)應(yīng)TriggerKey;2、startAt()endAt()對(duì)應(yīng)啟止時(shí)間;3、withSchedule(CronScheduleBuilder.cronSchedule("*/10 * * * * ?"));4、forJob(JobKey keyOfJobToFire)TriggerJob進(jìn)行關(guān)聯(lián),這樣才知道觸發(fā)哪個(gè)任務(wù)。

最后通過SchedulerscheduleJob(Trigger trigger)方法就將創(chuàng)建的Trigger定義信息添加到quartz中,一般采用數(shù)據(jù)庫(kù)持久化模式,即這里就會(huì)將Trigger定義信息插入到觸發(fā)器相關(guān)表中,示例中使用cron觸發(fā)器,則插入到qrtz_cron_triggers表中(見下圖)。

下面我們就來看下任務(wù)是咋個(gè)觸發(fā)的。SchedulerscheduleJob(Trigger trigger)將觸發(fā)器持久化后,你會(huì)發(fā)現(xiàn)qrtz_cron_triggers中沒有起止時(shí)間以及和Job綁定內(nèi)容,所以,接下來我們看一張非常重要表:qrtz_triggers。scheduleJob()方法在持久化Trigger信息后會(huì)同時(shí)向qrtz_triggers表插入一條記錄(見下圖):

qrtz_job_detailsqrtz_cron_triggers可以看成靜態(tài)表,那qrtz_triggers就是運(yùn)行動(dòng)態(tài)表,保存著任務(wù)運(yùn)行期間數(shù)據(jù),且隨著運(yùn)行記錄在動(dòng)態(tài)變更,是quartz調(diào)度任務(wù)運(yùn)行最重要的一張表,下面我們來看下這張表中幾個(gè)關(guān)鍵字段:

start_time、end_time: trigger定義時(shí)設(shè)置的起止時(shí)間next_fire_time: 下次觸發(fā)時(shí)間戳prev_fire_time: 上次觸發(fā)時(shí)間戳trigger_state: trigger狀態(tài),最常見狀態(tài)WAITING、ACQUIRED和EXECUTING,分別對(duì)應(yīng)等待(下次觸發(fā)時(shí)間還早) -> 加載到內(nèi)存中等待(下次觸發(fā)時(shí)間快到了) --> 執(zhí)行(下次觸發(fā)時(shí)間到了,需要觸發(fā)任務(wù)),具體參見后續(xù)源碼分析misfire_instr: trigger觸發(fā)時(shí)間過期處理策略,比如本來是10:23:50時(shí)間點(diǎn)進(jìn)行觸發(fā),但是由于某些原因在10:23:53秒才檢索出來,這是該觸發(fā)時(shí)間點(diǎn)已經(jīng)過期,misfire_instr就是控制采用什么策略處理該過期任務(wù),是直接丟棄重新計(jì)算下次觸發(fā)時(shí)間點(diǎn)、還是一定時(shí)間范圍內(nèi)過期的理解執(zhí)行等等,具體參見后續(xù)源碼分析job_data: 和JobDetail一樣,Trigger也可綁定一個(gè)JobDataMap,用于向Job實(shí)例傳遞參數(shù),該字段就是存儲(chǔ)Trigger關(guān)聯(lián)的JobDataMap序列化內(nèi)容

quartz基本上就是圍繞qrtz_triggers中這幾個(gè)關(guān)鍵字段實(shí)現(xiàn)任務(wù)觸發(fā),我們連蒙帶猜大致可以想出quartz任務(wù)調(diào)度觸發(fā)機(jī)制粗略流程:

1、通過配置的trigger觸發(fā)器,計(jì)算出下次觸發(fā)時(shí)間,更新到next_fire_time字段,同時(shí)更新trigger_state狀態(tài)為WAITING;

2、quartz線程掃描該表,從表中查詢出未來很短一段時(shí)間將要觸發(fā)的記錄(比對(duì)next_fire_time和當(dāng)前時(shí)間)放入到內(nèi)存排隊(duì)隊(duì)列中,然后將trigger_state更新成ACQUIRED;

3、然后阻塞直到內(nèi)存排隊(duì)隊(duì)列中觸發(fā)任務(wù)到時(shí)間點(diǎn),再觸發(fā)任務(wù)之前,重新計(jì)算下次觸發(fā)時(shí)間點(diǎn),更新到next_fire_time,同時(shí)將trigger_state更新為WAITING,然后執(zhí)行當(dāng)前任務(wù);

4、由于next_fire_timetrigger_state值更新,重新開始步驟1,就這樣循環(huán)往復(fù)觸發(fā)下去。

總結(jié)

這節(jié)從一個(gè)使用者角度簡(jiǎn)單分析quartz核心運(yùn)行機(jī)制,由于只是簡(jiǎn)單的從外層而未深入剖析源碼,只是簡(jiǎn)單結(jié)合數(shù)據(jù)庫(kù)表信息對(duì)quartz大致的運(yùn)行機(jī)制做個(gè)簡(jiǎn)單猜想,一些重要屬性也沒展開,帶著這些疑問下一節(jié)通過源碼分析找到真實(shí)的答案,一步步加深對(duì)quartz運(yùn)行機(jī)制的理解。

關(guān)鍵詞:

相關(guān)新聞