一、定時(shí)任務(wù)概述
1.1 定時(shí)任務(wù)認(rèn)識(shí)
1.1.1 什么是定時(shí)任務(wù)
定時(shí)任務(wù)是按照指定時(shí)間周期運(yùn)行任務(wù)。使用場(chǎng)景為在某個(gè)固定時(shí)間點(diǎn)執(zhí)行,或者周期性的去執(zhí)行某個(gè)任務(wù),比如:每天晚上24點(diǎn)做數(shù)據(jù)匯總,定時(shí)發(fā)送短信等。
1.1.2 常見(jiàn)定時(shí)任務(wù)方案
- While + Sleep : 通過(guò)循環(huán)加休眠的方式定時(shí)執(zhí)行
- Timer和TimerTask實(shí)現(xiàn) :JDK自帶的定時(shí)任務(wù),可以實(shí)現(xiàn)簡(jiǎn)單的間隔執(zhí)行任務(wù)(在指定時(shí)間點(diǎn)執(zhí)行某一任務(wù),也能定時(shí)的周期性執(zhí)行),無(wú)法實(shí)現(xiàn)按日歷去調(diào)度執(zhí)行任務(wù)。
- ScheduledExecutorService : Java并發(fā)包下,JDK1.5出現(xiàn),是比較理想的定時(shí)任務(wù)實(shí)現(xiàn)方案。Eureka就使用的是它
- QuartZ : 使用Quartz,它是一個(gè)異步任務(wù)調(diào)度框架,功能豐富,可以實(shí)現(xiàn)按日歷調(diào)度,支持持久化。
- 使用Spring Task,Spring 3.0后提供Spring Task實(shí)現(xiàn)任務(wù)調(diào)度,支持按日歷調(diào)度,相比Quartz功能稍簡(jiǎn)單,但是在開(kāi)發(fā)基本夠用,支持注解編程方式。
- SpringBoot中的Schedule : 通過(guò)@EnableScheduling+@Scheduled最實(shí)現(xiàn)定時(shí)任務(wù),底層使用的是Spring Task
1.2 分布式定時(shí)任務(wù)
1.2.1 遇到什么問(wèn)題
上述的定時(shí)任務(wù)都是集中式(單體項(xiàng)目使用)的定時(shí)任務(wù),在分布式中將會(huì)面臨一些問(wèn)題或不足
1、業(yè)務(wù)量大,單機(jī)性能瓶頸需要擴(kuò)展
2、多臺(tái)機(jī)器部署如何保證定時(shí)任務(wù)不重復(fù)執(zhí)行
3、定時(shí)任務(wù)時(shí)間需要可調(diào)整,可以暫停
4、機(jī)器發(fā)生故障down機(jī),定時(shí)任務(wù)依然可用,如何實(shí)現(xiàn)故障轉(zhuǎn)移
5、定時(shí)任務(wù),執(zhí)行日志是否可監(jiān)控
1.2.2 分布式定時(shí)任務(wù)xxl-job
XXL-JOB是一個(gè)分布式任務(wù)調(diào)度平臺(tái),于2015問(wèn)世,其核心設(shè)計(jì)目標(biāo)是開(kāi)發(fā)迅速、學(xué)習(xí)簡(jiǎn)單、輕量級(jí)、易擴(kuò)展。現(xiàn)已開(kāi)放源代碼并接入多家公司線上產(chǎn)品線,開(kāi)箱即用。其具備且不止如下能力
- 簡(jiǎn)單:支持通過(guò)Web頁(yè)面對(duì)任務(wù)進(jìn)行CRUD操作,操作簡(jiǎn)單,一分鐘上手;
- 動(dòng)態(tài):支持動(dòng)態(tài)修改任務(wù)狀態(tài)、啟動(dòng)/停止任務(wù),以及終止運(yùn)行中任務(wù),即時(shí)生效;
- 調(diào)度中心HA(中心式):調(diào)度采用中心式設(shè)計(jì),“調(diào)度中心”基于集群Quartz實(shí)現(xiàn)并支持集群部署,可保證調(diào)度中心HA;執(zhí)行器HA(分布式):任務(wù)分布式執(zhí)行,任務(wù)"執(zhí)行器"支持集群部署,可保證任務(wù)執(zhí)行HA;
- 彈性擴(kuò)容縮容:一旦有新執(zhí)行器機(jī)器上線或者下線,下次調(diào)度時(shí)將會(huì)重新分配任務(wù);
- 路由策略:執(zhí)行器集群部署時(shí)提供豐富的路由策略,包括:第一個(gè)、最后一個(gè)、輪詢、隨機(jī)、一致性HASH、最不經(jīng)常使用、最近最久未使用、故障轉(zhuǎn)移、忙碌轉(zhuǎn)移等;
- 故障轉(zhuǎn)移:任務(wù)路由策略選擇"故障轉(zhuǎn)移"情況下,如果執(zhí)行器集群中某一臺(tái)機(jī)器故障,將會(huì)自動(dòng)Failover切換到一臺(tái)正常的執(zhí)行器發(fā)送調(diào)度請(qǐng)求。
- 任務(wù)失敗告警:默認(rèn)提供郵件方式失敗告警,同時(shí)預(yù)留擴(kuò)展接口,可方面的擴(kuò)展短信、釘釘?shù)雀婢绞健?/li>
二、XXL-JOB初體驗(yàn)
2.1 官網(wǎng)
2.2 xxl-job架構(gòu)設(shè)計(jì)
2.2.1 設(shè)計(jì)思想
將調(diào)度行為抽象形成“調(diào)度中心”公共平臺(tái),而平臺(tái)自身并不承擔(dān)業(yè)務(wù)邏輯,“調(diào)度中心”負(fù)責(zé)發(fā)起調(diào)度請(qǐng)求。將任務(wù)抽象成分散的JobHandler,交由“執(zhí)行器”統(tǒng)一管理,“執(zhí)行器”負(fù)責(zé)接收調(diào)度請(qǐng)求并執(zhí)行對(duì)應(yīng)的JobHandler中業(yè)務(wù)邏輯。因此,“調(diào)度”和“任務(wù)”兩部分可以相互解耦,提高系統(tǒng)整體穩(wěn)定性和擴(kuò)展性。
2.2.2 架構(gòu)設(shè)計(jì)圖
xxl-job分為 調(diào)度中心和執(zhí)行器兩大模塊
1.調(diào)度模塊(調(diào)度中心)
負(fù)責(zé)管理調(diào)度信息,按照調(diào)度配置發(fā)出調(diào)度請(qǐng)求,自身不承擔(dān)業(yè)務(wù)代碼。調(diào)度系統(tǒng)與任務(wù)解耦,提高了系統(tǒng)可用性和穩(wěn)定性,同時(shí)調(diào)度系統(tǒng)性能不再受限于任務(wù)模塊;支持可視化、簡(jiǎn)單且動(dòng)態(tài)的管理調(diào)度信息,包括任務(wù)新建,更新,刪除,GLUE開(kāi)發(fā)和任務(wù)報(bào)警等,所有上述操作都會(huì)實(shí)時(shí)生效,同時(shí)支持監(jiān)控調(diào)度結(jié)果以及執(zhí)行日志,支持執(zhí)行器Failover(故障轉(zhuǎn)移)。
2.執(zhí)行模塊(執(zhí)行器)
負(fù)責(zé)接收調(diào)度請(qǐng)求并執(zhí)行任務(wù)邏輯。任務(wù)模塊專(zhuān)注于任務(wù)的執(zhí)行等操作,開(kāi)發(fā)和維護(hù)更加簡(jiǎn)單和高效;接收“調(diào)度中心”的執(zhí)行請(qǐng)求、終止請(qǐng)求和日志請(qǐng)求等。
3.調(diào)度中心高可用
基于數(shù)據(jù)庫(kù)的集群方案,數(shù)據(jù)庫(kù)選用Mysql;集群分布式并發(fā)環(huán)境中進(jìn)行定時(shí)任務(wù)調(diào)度時(shí),會(huì)在各個(gè)節(jié)點(diǎn)會(huì)上報(bào)任務(wù),存到數(shù)據(jù)庫(kù)中,執(zhí)行時(shí)會(huì)從數(shù)據(jù)庫(kù)中取出觸發(fā)器來(lái)執(zhí)行,如果觸發(fā)器的名稱和執(zhí)行時(shí)間相同,則只有一個(gè)節(jié)點(diǎn)去執(zhí)行此任務(wù)。
4.并行調(diào)度
調(diào)度采用線程池方式實(shí)現(xiàn),避免單線程因阻塞而引起任務(wù)調(diào)度延遲。XXL-JOB調(diào)度模塊默認(rèn)采用并行機(jī)制,在多線程調(diào)度的情況下,調(diào)度模塊被阻塞的幾率很低,大大提高了調(diào)度系統(tǒng)的承載量。XXL-JOB的不同任務(wù)之間并行調(diào)度、并行執(zhí)行。XXL-JOB的單個(gè)任務(wù),針對(duì)多個(gè)執(zhí)行器是并行運(yùn)行的,針對(duì)單個(gè)執(zhí)行器是串行執(zhí)行的。同時(shí)支持任務(wù)終止。
5.執(zhí)行器(任務(wù))高可用
執(zhí)行器如若集群部署,調(diào)度中心將會(huì)感知到在線的所有執(zhí)行器,如“127.0.0.1:9997, 127.0.0.1:9998, 127.0.0.1:9999”。多個(gè)執(zhí)行器可以選擇“路由策略”來(lái)采用輪詢,隨機(jī)等方式進(jìn)行多機(jī)器調(diào)度。當(dāng)任務(wù)”路由策略”選擇”故障轉(zhuǎn)移(FAILOVER)”時(shí),當(dāng)調(diào)度中心每次發(fā)起調(diào)度請(qǐng)求時(shí),會(huì)按照順序?qū)?zhí)行器發(fā)出心跳檢測(cè)請(qǐng)求,第一個(gè)檢測(cè)為存活狀態(tài)的執(zhí)行器將會(huì)被選定并發(fā)送調(diào)度請(qǐng)求。調(diào)度成功后,可在日志監(jiān)控界面查看“調(diào)度備注”
2.3 xxl-job安裝
2.3.1 下載源碼
請(qǐng)下載項(xiàng)目源碼并解壓,使用IDEA工具導(dǎo)入項(xiàng)目,源碼倉(cāng)庫(kù)地址,
https://github.com/xuxueli/xxl-jobhttp://gitee.com/xuxueli0323/xxl-job
項(xiàng)目代碼結(jié)構(gòu)如下:

- doc :文檔,即SQL腳本所在目錄
- db : “調(diào)度數(shù)據(jù)庫(kù)”建表腳本
- xxl-job-admin : 調(diào)度中心項(xiàng)目源碼
- xxl-job-core : 核心模塊,公共Jar依賴
- xxl-job-executor-samples : 執(zhí)行器,Sample示例項(xiàng)目(大家可以在該項(xiàng)目上進(jìn)行開(kāi)發(fā),也可以將現(xiàn)有項(xiàng)目改造生成執(zhí)行器項(xiàng)目)
2.3.2 導(dǎo)入數(shù)據(jù)庫(kù)
打開(kāi)項(xiàng)目代碼,獲取 “調(diào)度數(shù)據(jù)庫(kù)初始化SQL腳本” 并執(zhí)行即可?!罢{(diào)度數(shù)據(jù)庫(kù)初始化SQL腳本” 位置為: /xxl-job/doc/db/tables_xxl_job.sql ,數(shù)據(jù)庫(kù)名:xxl_job
- xxl_job_lock:任務(wù)調(diào)度鎖表;
- xxl_job_group:執(zhí)行器信息表,維護(hù)任務(wù)執(zhí)行器信息;
- xxl_job_info:調(diào)度擴(kuò)展信息表:用于保存XXL-JOB調(diào)度任務(wù)的擴(kuò)展信息,如任務(wù)分組、任務(wù)名、機(jī)器地址、執(zhí)行器、執(zhí)行入?yún)⒑蛨?bào)警郵件等等;
- xxl_job_log:調(diào)度日志表:用于保存XXL-JOB任務(wù)調(diào)度的歷史信息,如調(diào)度結(jié)果、執(zhí)行結(jié)果、調(diào)度入?yún)?、調(diào)度機(jī)器和執(zhí)行器等等;
- xxl_job_log_report:調(diào)度日志報(bào)表:用戶存儲(chǔ)XXL-JOB任務(wù)調(diào)度日志的報(bào)表,調(diào)度中心報(bào)表功能頁(yè)面會(huì)用到;
- xxl_job_logglue:任務(wù)GLUE日志:用于保存GLUE更新歷史,用于支持GLUE的版本回溯功能;
- xxl_job_registry:執(zhí)行器注冊(cè)表,維護(hù)在線的執(zhí)行器和調(diào)度中心機(jī)器地址信息;
- xxl_job_user:系統(tǒng)用戶表;
2.3.3 啟動(dòng)調(diào)度中心
打開(kāi) xxl-job-admin 的配置文件,/xxl-job/xxl-job-admin/src/main/resources/application.properties 對(duì)調(diào)度中心進(jìn)行配置,重要配置如下
- server.port : 根據(jù)情況修改端口
- spring.datasource.url :指向剛才準(zhǔn)備的數(shù)據(jù)庫(kù)
- spring.datasource.password : 記得修改成自己的數(shù)據(jù)庫(kù)密碼
- spring.mail.username :配置自己的郵件賬號(hào)
- spring.mail.password :郵件的授權(quán)碼,我下面是以qq郵箱為例
然后啟動(dòng)調(diào)度中心 ,執(zhí)行 XxlJobAdminApplication#main 方法 , 啟動(dòng)之后,瀏覽器訪問(wèn) http://localhost:18080/xxl-job-admin/jobinfo?jobGroup=2 ;注意URL中有個(gè)上下文路徑。默認(rèn)登錄賬號(hào) “admin/123456”, 登錄后運(yùn)行界面如下圖所示。

2.3.4 配置部署“執(zhí)行器項(xiàng)目
“執(zhí)行器”項(xiàng)目:xxl-job-executor-sample-springboot (提供多種版本執(zhí)行器供選擇,現(xiàn)以 springboot 版本為例,可直接使用,也可以參考其并將現(xiàn)有項(xiàng)目改造成執(zhí)行器)
- 作用:負(fù)責(zé)接收“調(diào)度中心”的調(diào)度并執(zhí)行;可直接部署執(zhí)行器,也可以將執(zhí)行器集成到現(xiàn)有業(yè)務(wù)項(xiàng)目中。
- 修改配置:/xxl-job/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/application.properties
- xxl.job.admin.addresses : 調(diào)度中心的地址,如果調(diào)度中心修改過(guò)端口,這里也要對(duì)應(yīng)修改
在com.xxl.job.executor.service.jobhandler.SampleXxlJob中提供了簡(jiǎn)單的定時(shí)任務(wù)實(shí)例,
為方便用戶參考與快速實(shí)用,示例執(zhí)行器內(nèi)原生提供多個(gè)Bean模式任務(wù)Handler,可以直接配置實(shí)用,如下:
- demoJobHandler:簡(jiǎn)單示例任務(wù),任務(wù)內(nèi)部模擬耗時(shí)任務(wù)邏輯,用戶可在線體驗(yàn)Rolling Log等功能;
- shardingJobHandler:分片示例任務(wù),任務(wù)內(nèi)部模擬處理分片參數(shù),可參考熟悉分片任務(wù);
- httpJobHandler:通用HTTP任務(wù)Handler;業(yè)務(wù)方只需要提供HTTP鏈接等信息即可,不限制語(yǔ)言、平臺(tái)。
2.4 配置定時(shí)任務(wù)
2.4.1 執(zhí)行器創(chuàng)建
打開(kāi)調(diào)度中心可視化界面,在執(zhí)行器管理界面,添加新增執(zhí)行器

- appName : 執(zhí)行器的名字,可以任意填寫(xiě)
- 名稱:任意填寫(xiě)
- 注冊(cè)方式:調(diào)度中心是通過(guò)RPC的方式對(duì)執(zhí)行器發(fā)起調(diào)度,所以這里需要的是執(zhí)行器項(xiàng)目的ip:port ,注意,該端口不是執(zhí)行器項(xiàng)目的server.port ,而是:xxl.job.executor.port 端口。你可以選擇自動(dòng)注冊(cè),也可以手動(dòng)錄入。
2.4.2 創(chuàng)建任務(wù)
在任務(wù)管理界面,新增任務(wù)

- 路由策略:有輪詢,隨機(jī),故障轉(zhuǎn)移等等策略,是用在集群模式下的調(diào)度方式。
- cron : 定時(shí)任務(wù)的執(zhí)行時(shí)間規(guī)則,時(shí)間表達(dá)式
- JobHandler : 這個(gè)是要對(duì)應(yīng) “執(zhí)行器項(xiàng)目”中 @XxlJob("demoJobHandler") 注解中的名字
- 運(yùn)行模式 :Bean ,使用內(nèi)置代碼方式,也可以執(zhí)行在線執(zhí)行代碼方式
- 報(bào)警郵件 :如果定時(shí)任務(wù)失敗,會(huì)發(fā)送報(bào)警郵件到郵箱
- 任務(wù)參數(shù):這個(gè)參數(shù)可以傳遞給 @XxlJob("demoJobHandler")所在方法的參數(shù)。
創(chuàng)建好任務(wù)之后就可以執(zhí)行了

調(diào)度日志

2.5 GLUE模式(Java)
2.5.1 添加任務(wù)
該模式支持在線編輯定時(shí)任務(wù)的內(nèi)容,立刻執(zhí)行,無(wú)需再開(kāi)發(fā)工具中編輯代碼,也無(wú)需重啟項(xiàng)目。請(qǐng)點(diǎn)擊任務(wù)右側(cè) “GLUE” 按鈕,進(jìn)入 “GLUE編輯器開(kāi)發(fā)界面” ,見(jiàn)下圖?!癎LUE模式(Java)” 運(yùn)行模式的任務(wù)默認(rèn)已經(jīng)初始化了示例任務(wù)代碼,即打印Hello World。

任務(wù)以源碼方式維護(hù)在調(diào)度中心,支持通過(guò)Web IDE在線更新,實(shí)時(shí)編譯和生效,因此不需要指定JobHandler
2.5.2 編寫(xiě)代碼
保存之后可以在操作按鈕里面去編寫(xiě)任務(wù)

(“GLUE模式(Java)” 運(yùn)行模式的任務(wù)實(shí)際上是一段繼承自IJobHandler的Java類(lèi)代碼,它在執(zhí)行器項(xiàng)目中運(yùn)行,可使用@Resource/@Autowire注入執(zhí)行器里中的其他服務(wù)),比如我的定時(shí)任務(wù)如下,編輯好之后點(diǎn)擊保存

保存好之后,啟動(dòng)定時(shí)任務(wù),效果如下

2.6 GLUE(Shell)



三、XXL-JOB集群部署
3.1 調(diào)度中心集群
3.1.1 問(wèn)題概述
調(diào)度中心支持集群部署,提升調(diào)度系統(tǒng)容災(zāi)和可用性。調(diào)度中心集群部署時(shí),幾點(diǎn)要求和建議:
- DB配置保持一致;
- 集群機(jī)器時(shí)鐘保持一致(單機(jī)集群忽視);
- 當(dāng)啟動(dòng)多個(gè)調(diào)度器時(shí),執(zhí)行器配置調(diào)度中心部署跟地址可以用逗號(hào)分隔。執(zhí)行器將會(huì)使用該地址進(jìn)行"執(zhí)行器心跳注冊(cè)"和"任務(wù)結(jié)果回調(diào)";為空則關(guān)閉自動(dòng)注冊(cè);
- 但是建議:推薦通過(guò)nginx為調(diào)度中心集群做負(fù)載均衡,分配域名。調(diào)度中心訪問(wèn)、執(zhí)行器回調(diào)配置、調(diào)用API服務(wù)等操作均通過(guò)該域名進(jìn)行。
3.1.2 啟動(dòng)多個(gè)調(diào)度中心
修改調(diào)度中心端口,啟動(dòng)多個(gè)調(diào)度中心,我這里啟動(dòng)兩個(gè)如
http://localhost:18080/xxl-job-admin/http://localhost:18081/xxl-job-admin/
3.1.3 配置Nginx負(fù)載均衡
當(dāng)啟動(dòng)多個(gè)調(diào)度器時(shí),執(zhí)行器配置調(diào)度中心部署跟地址可以用逗號(hào)分隔。執(zhí)行器將會(huì)使用該地址進(jìn)行“執(zhí)行器心跳注冊(cè)”和“任務(wù)結(jié)果回調(diào)”;為空則關(guān)閉自動(dòng)注冊(cè)。但是建議:推薦通過(guò)nginx為調(diào)度中心集群做負(fù)載均衡,分配域名。調(diào)度中心訪問(wèn)、執(zhí)行器回調(diào)配置、調(diào)用API服務(wù)等操作均通過(guò)該域名進(jìn)行。
四、最后
xxl-job確實(shí)很強(qiáng)大,功能也很全,經(jīng)過(guò)該文章學(xué)習(xí)相信你可以把xxl-job給用起來(lái)了,但是如果你的項(xiàng)目是一個(gè)小體量的單體,我不太建議使用它,Quzrtz或者SpringBoot Task就足夠 ,對(duì)于xxl-job個(gè)人還是有些笨重。
轉(zhuǎn)載自:分布式定時(shí)調(diào)度-xxl-job