作為一個(gè)支付公司的項(xiàng)目組,經(jīng)常會(huì)有很多對(duì)賬功能(簽約對(duì)賬豌鹤、支付訂單對(duì)賬亡哄、記賬對(duì)賬),這些都是以定時(shí)任務(wù)的形式實(shí)現(xiàn)布疙,組內(nèi)經(jīng)常需要維護(hù)一些定時(shí)任務(wù)蚊惯,在使用過程中主要關(guān)注定時(shí)任務(wù)的穩(wěn)定性、健壯性和可控性灵临,分布式定時(shí)任務(wù)+任務(wù)監(jiān)控基本滿足使用需求拣挪。
定時(shí)任務(wù)的現(xiàn)狀
就定時(shí)任務(wù)來說,首先是操作系統(tǒng)層面一直支持的功能俱诸,所以我們的各種對(duì)定時(shí)任務(wù)的實(shí)現(xiàn)手段才能得以發(fā)揮菠劝。由于操作系統(tǒng)和編程語言種類繁多,本文中將重點(diǎn)從linux操作系統(tǒng)、java語言以及java生態(tài)中開源框架來介紹定時(shí)任務(wù)赶诊。
1. linux系統(tǒng)級(jí)的定時(shí)任務(wù)執(zhí)行器:crontab
這是一個(gè)系統(tǒng)級(jí)的定時(shí)器笼平,具有靈活簡(jiǎn)單的特性,同時(shí)也是缺乏管理功能和分布式支持舔痪。一般的使用方法是寓调,使用shell腳本調(diào)用業(yè)務(wù)實(shí)現(xiàn)jar包。
2. java語言中的定時(shí)器:java.util.Timer
這是一個(gè)java語言內(nèi)置的定時(shí)器锄码,這是所有基于java語言的開源框架實(shí)現(xiàn)的基礎(chǔ)夺英。其中,典型的任務(wù)調(diào)度方法:
public void schedule(TimerTask task, long delay);
public void schedule(TimerTask task, Date time);
public void schedule(TimerTask task, long delay, long period);
public void schedule(TimerTask task, Date firstTime, long period);
public void scheduleAtFixedRate(TimerTask task, long delay, long period);
public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period);
3. java定時(shí)任務(wù)開源框架(常用)
3.1. Quartz(java定時(shí)任務(wù)的標(biāo)準(zhǔn))
3.2. spring task
在spring中實(shí)現(xiàn)定時(shí)任務(wù)有兩種方式滋捶,一是spring對(duì)quartz進(jìn)行了封裝痛悯,二是獨(dú)立實(shí)現(xiàn)了定時(shí)任務(wù)的調(diào)度器稱為spring task
。其中spring task相對(duì)較為簡(jiǎn)單重窟,cron表達(dá)式不支持L W
载萌;而quartz不但完整支持cron表達(dá)式,而且具有很多高級(jí)特性巡扇,如JDBCJobStore(作業(yè)倉(cāng)庫(kù))扭仁、集群模式。
4. 分布式定時(shí)任務(wù)開源框架
4.1. Elastic-Job(當(dāng)當(dāng)網(wǎng))
4.2. light-task-scheduler
4.3. clover
4.4. TBSchedule(阿里)
4.5. niubi-job
4.6. Uncode-Schedule
基于java的分布式定時(shí)任務(wù)的開源框架還是很多的厅翔,這里沒有一一列舉乖坠,有些框架設(shè)計(jì)的非常完善復(fù)雜,可根據(jù)具體使用場(chǎng)景選擇適當(dāng)?shù)目蚣艿睹疲?dāng)某些功能不符合你的預(yù)期的時(shí)候熊泵,可以去修改一下源代碼實(shí)現(xiàn)自己想要的功能,這是開源框架的好處涩赢。
根據(jù)我們所有定時(shí)任務(wù)的特點(diǎn)和實(shí)際運(yùn)行需求戈次,我選擇了Uncode-Schedule框架轩勘,這個(gè)框架相對(duì)簡(jiǎn)單筒扒、靈活,并且修改源代碼以滿足特定需求相對(duì)容易绊寻。
分布式定時(shí)任務(wù)面臨的問題
對(duì)于分布式應(yīng)用來說花墩,一致性始終是首要問題,另外就是對(duì)任務(wù)和執(zhí)行任務(wù)的機(jī)器的管理問題澄步。
1. 一致性問題
1.1. 一個(gè)任務(wù)不能同時(shí)在多臺(tái)機(jī)器上執(zhí)行
1.2. 當(dāng)某一臺(tái)執(zhí)行任務(wù)的機(jī)器宕機(jī)時(shí)冰蘑,該機(jī)器上的任務(wù)自動(dòng)重新分配至其他機(jī)器
1.3. 任務(wù)的均衡分配
1.4. 心跳檢測(cè)
2. 任務(wù)的管理和監(jiān)控
2.1. 任務(wù)的動(dòng)態(tài)添加和刪除
2.2. 控制執(zhí)行任務(wù)的機(jī)器
2.3. 隨時(shí)手動(dòng)執(zhí)行任務(wù)
Uncode-Schedule開源框架分析
Uncode-Schedule是一個(gè)實(shí)現(xiàn)分布式定時(shí)任務(wù)的開源框架,java語言實(shí)現(xiàn)村缸,本章將從上面的幾個(gè)問題入手分析該框架的實(shí)現(xiàn)機(jī)制祠肥。
1. 功能概述
Uncode-Schedule是基于zookeeper的分布式任務(wù)調(diào)度組件,非常小巧梯皿,使用簡(jiǎn)單仇箱,開發(fā)時(shí)只需要引入jar包县恕,不需要單獨(dú)部署服務(wù)端。而且定時(shí)任務(wù)的實(shí)現(xiàn)方式和原始的spring/quartz task保持一致剂桥,所以普通的定時(shí)任務(wù)改造成分布式很方便忠烛。
1.1. 它能夠確保所有任務(wù)在集群中不重復(fù),不遺漏的執(zhí)行权逗。
1.2. 單節(jié)點(diǎn)故障時(shí)美尸,任務(wù)能夠自動(dòng)轉(zhuǎn)移到其他節(jié)點(diǎn)繼續(xù)執(zhí)行。
1.3. 支持動(dòng)態(tài)添加和刪除任務(wù)斟薇。
1.4. 支持添加機(jī)器ip黑名單师坎。
1.5. 支持手動(dòng)執(zhí)行任務(wù)。
2. 注意事項(xiàng)
在使用Uncode-Schedule框架開發(fā)定時(shí)任務(wù)時(shí)奔垦,需要注意幾個(gè)問題:
2.1. 開發(fā)的定時(shí)任務(wù)在業(yè)務(wù)要保證冪等性屹耐。主要是為了解決單點(diǎn)故障時(shí),任務(wù)轉(zhuǎn)移重復(fù)執(zhí)行帶來的問題椿猎。
2.2. 任務(wù)節(jié)點(diǎn)啟動(dòng)時(shí)必須保證zookeeper可用惶岭。
2.3. 任務(wù)節(jié)點(diǎn)運(yùn)行期zookeeper集群不可用時(shí)任務(wù)節(jié)點(diǎn)保持可用前狀態(tài)運(yùn)行,當(dāng)zookeeper集群恢復(fù)后犯眠,任務(wù)正常運(yùn)行按灶。
2.4. 手動(dòng)執(zhí)行任務(wù)時(shí),需要選擇在哪臺(tái)機(jī)器執(zhí)行筐咧。
3. 模塊架構(gòu)
3.1. Uncode-Schedule分布式定時(shí)任務(wù)的設(shè)計(jì)結(jié)構(gòu)如下圖:
zookeeper集群負(fù)責(zé)存儲(chǔ)定時(shí)任務(wù)和機(jī)器的數(shù)據(jù)鸯旁,起到存儲(chǔ)數(shù)據(jù)和保持一致性的關(guān)鍵作用。在所有的執(zhí)行任務(wù)的節(jié)點(diǎn)中有一個(gè)leader量蕊,它會(huì)負(fù)責(zé)任務(wù)的分配铺罢。任務(wù)的管理和監(jiān)控平臺(tái)可單獨(dú)部署。
3.2. 每一個(gè)任務(wù)節(jié)點(diǎn)的內(nèi)部結(jié)構(gòu)如下圖:
執(zhí)行任務(wù)的節(jié)點(diǎn)上支持的任務(wù)類型包括spring的封裝的quartz任務(wù)和spring task残炮;另外該框架還自定義了一種任務(wù)Uncode Task韭赘,實(shí)際上是一種由zookeeper存儲(chǔ)調(diào)度信息的任務(wù),最終還是由spring的調(diào)度器
ThreadPoolTaskScheduler
控制的势就,不過這種任務(wù)是可動(dòng)態(tài)添加和刪除的泉瞻。
3. 使用方法
Uncode-Schedule框架的使用方法是很簡(jiǎn)單的,而且我也沒有對(duì)原作者的使用方法做修改苞冯,所以具體使用方法可參考github上的README文檔袖牙。
我根據(jù)對(duì)框架的功能完善,自己設(shè)計(jì)了一個(gè)任務(wù)的管理和監(jiān)控平臺(tái)舅锄。主要在其中添加了手動(dòng)執(zhí)行的功能鞭达,其他的監(jiān)控功能會(huì)進(jìn)一步完善。
4. 源碼分析
見下一篇文章。