以下將分別從Quartz架構(gòu)簡(jiǎn)介、集群部署實(shí)踐、Quartz監(jiān)控、集群原理分析詳解Quartz任務(wù)調(diào)度框架。
Quartz是Java領(lǐng)域最著名的開(kāi)源任務(wù)調(diào)度工具,是一個(gè)任務(wù)調(diào)度框架,通過(guò)觸發(fā)器設(shè)置作業(yè)的定時(shí)運(yùn)行規(guī)則,來(lái)執(zhí)行定時(shí)任務(wù)。其中quartz集群通過(guò)故障切換和負(fù)載平衡的功能,能給調(diào)度器帶來(lái)高可用性和伸縮性。
Quartz提供了極為廣泛的特性如持久化任務(wù),集群和分布式任務(wù)等。
其特點(diǎn)如下:
典型的使用場(chǎng)景,主要用來(lái)執(zhí)行定時(shí)任務(wù),例如:
Quartz框架主要核心組件包括:
1.Scheduler任務(wù)調(diào)度
是最核心的概念,需要把JobDetail和Trigger注冊(cè)到scheduler中,才可以執(zhí)行。
工廠模式,組裝各個(gè)組件
sched.scheduleJob(job, trigger);
2.Job任務(wù)
其實(shí)Job是接口,其中只有一個(gè)execute方法,我們只需要 implements 此接口,重寫(xiě) execute(*) 方法。
3.Trigger觸發(fā)器
執(zhí)行任務(wù)的規(guī)則;比如每天,每小時(shí)等。
一般情況使用SimpleTrigger,和CronTrigger,這些觸發(fā)器實(shí)現(xiàn)了Trigger接口?;蛘?ScheduleBuilder 子類 SimpleScheduleBuilder和CronScheduleBuilder。
對(duì)于簡(jiǎn)單的時(shí)間來(lái)說(shuō),比如每天執(zhí)行幾次,使用SimpleTrigger。
對(duì)于復(fù)雜的時(shí)間表達(dá)式來(lái)說(shuō),比如每個(gè)月15日上午幾點(diǎn)幾分,使用CronTrigger以及CromExpression 類。
4.JobDetail任務(wù)細(xì)節(jié)
任務(wù)細(xì)節(jié),Quartz執(zhí)行Job時(shí),需要新建個(gè)Job實(shí)例,但是不能直接操作Job類,所以通過(guò)JobDetail來(lái)獲取Job的名稱、描述信息。
調(diào)度器作為作業(yè)的總指揮,觸發(fā)器作為作業(yè)的操作者,作業(yè)為應(yīng)用的功能模塊。
Quartz與Spring結(jié)合使用,Spring通過(guò)提供org.springframework.scheduling.quartz下的封裝類對(duì)Quartz支持。
1.Quartz集群部署:
Quartz集群中的每個(gè)節(jié)點(diǎn)是一個(gè)獨(dú)立的Quartz應(yīng)用,它又管理著其他的節(jié)點(diǎn)。該集群需要分別對(duì)每個(gè)節(jié)點(diǎn)分別啟動(dòng)或停止,不像應(yīng)用服務(wù)器的集群,獨(dú)立的Quartz節(jié)點(diǎn)并不與另一個(gè)節(jié)點(diǎn)或是管理節(jié)點(diǎn)通信。Quartz應(yīng)用是通過(guò)數(shù)據(jù)庫(kù)表來(lái)感知到另一應(yīng)用。只有使用持久的JobStore才能完成Quqrtz集群。
基于Spring的集群配置:
Quartz實(shí)例的監(jiān)控、操作以及動(dòng)態(tài)部署Trigger.
1.Triggers監(jiān)控:
2.JobDetails監(jiān)控:
1. Quartz集群數(shù)據(jù)庫(kù)表
Quartz的集群部署方案在架構(gòu)上是分布式的,沒(méi)有負(fù)責(zé)集中管理的節(jié)點(diǎn),而是利用數(shù)據(jù)庫(kù)鎖的方式來(lái)實(shí)現(xiàn)集群環(huán)境下進(jìn)行并發(fā)控制。BTW,分布式部署時(shí)需要保證各個(gè)節(jié)點(diǎn)的系統(tǒng)時(shí)間一致。
Quartz數(shù)據(jù)庫(kù)核心表如下:
2. Quartz線程模型
在Quartz中有兩類線程:
任務(wù)執(zhí)行線程:Quartz不會(huì)在主線程(QuartzSchedulerThread)中處理用戶的Job。
Quartz把線程管理的職責(zé)委托給ThreadPool,一般的設(shè)置使用SimpleThreadPool。SimpleThreadPool創(chuàng)建了一定數(shù)量的WorkerThread實(shí)例來(lái)使得Job能夠在線程中進(jìn)行處理。WorkerThread是定義在SimpleThreadPool類中的內(nèi)部類,它實(shí)質(zhì)上就是一個(gè)線程。例如,CRM中配置如下:
QuartzSchedulerThread調(diào)度主線程:QuartzScheduler被創(chuàng)建時(shí)創(chuàng)建一個(gè)QuartzSchedulerThread實(shí)例。
3.Quartz集群基于數(shù)據(jù)庫(kù)鎖的同步操作流程如下圖所示:
一個(gè)調(diào)度器實(shí)例在執(zhí)行涉及到分布式問(wèn)題的數(shù)據(jù)庫(kù)操作前,首先要獲取QUARTZ_LOCKS表中對(duì)應(yīng)的行級(jí)鎖,獲取鎖后即可執(zhí)行其他表中的數(shù)據(jù)庫(kù)操作,隨著操作事務(wù)的提交,行級(jí)鎖被釋放,供其他調(diào)度實(shí)例獲取。集群中的每一個(gè)調(diào)度器實(shí)例都遵循這樣一種嚴(yán)格的操作規(guī)程。
總結(jié)一下Quartz集群同步機(jī)制:每當(dāng)要進(jìn)行與某種業(yè)務(wù)相關(guān)的數(shù)據(jù)庫(kù)操作時(shí),先去QRTZ_LOCKS表中查詢操作相關(guān)的業(yè)務(wù)對(duì)象所需要的鎖,在select語(yǔ)句之后加for update來(lái)實(shí)現(xiàn)。例如,TRIGGER_ACCESS表示對(duì)任務(wù)觸發(fā)器相關(guān)的信息進(jìn)行修改、刪除操作時(shí)所需要獲得的鎖。這時(shí),執(zhí)行查詢這個(gè)表數(shù)據(jù)的SQL形如:
當(dāng)一個(gè)線程使用上述的SQL對(duì)表中的數(shù)據(jù)執(zhí)行查詢操作時(shí),若查詢結(jié)果中包含相關(guān)的行,數(shù)據(jù)庫(kù)就對(duì)該行進(jìn)行ROW LOCK;若此時(shí),另外一個(gè)線程使用相同的SQL對(duì)表的數(shù)據(jù)進(jìn)行查詢,由于查詢出的數(shù)據(jù)行已經(jīng)被數(shù)據(jù)庫(kù)鎖住了,此時(shí)這個(gè)線程就只能等待,直到擁有該行鎖的線程完成了相關(guān)的業(yè)務(wù)操作,執(zhí)行了commit動(dòng)作后,數(shù)據(jù)庫(kù)才會(huì)釋放了相關(guān)行的鎖,這個(gè)線程才能繼續(xù)執(zhí)行。
通過(guò)這樣的機(jī)制,在集群環(huán)境下,結(jié)合悲觀鎖的機(jī)制就可以防止一個(gè)線程對(duì)數(shù)據(jù)庫(kù)數(shù)據(jù)的操作的結(jié)果被另外一個(gè)線程所覆蓋,從而可以避免一些難以覺(jué)察的錯(cuò)誤發(fā)生。當(dāng)然,達(dá)到這種效果的前提是需要把Connection設(shè)置為手動(dòng)提交,即autoCommit為false。
聯(lián)系客服