java開發(fā)主流定時任務(wù)解決方案全橫評詳解
引言
定時任務(wù)作為一種按照約定時間執(zhí)行預(yù)期邏輯的通用模式,在企業(yè)級開發(fā)中承載著豐富的業(yè)務(wù)場景,諸如后臺定時同步數(shù)據(jù)生成報表,定時清理磁盤日志文件,定時掃描超時訂單進(jìn)行補償回調(diào)等。程序開發(fā)人員在定時任務(wù)領(lǐng)域有著諸多框架和方案可供選擇,并借此快速實現(xiàn)業(yè)務(wù)功能實現(xiàn)產(chǎn)品上線。本文將就當(dāng)前主流定時任務(wù)解決方案進(jìn)行介紹和分析,期望可以在企業(yè)技術(shù)選型和項目架構(gòu)重構(gòu)時作為參考。
Crontab
目標(biāo)定位
Crontab 作為 Linux 內(nèi)置的可執(zhí)行命令,可以實現(xiàn)按照 cron 表達(dá)式生成的時間執(zhí)行指定的系統(tǒng)指令或 shell 腳本。
使用方式
crontab 命令語法:
crontab [-u username] [-l | -e | -r ] 參數(shù): -u : 只有root用戶才能進(jìn)行這個任務(wù),編輯某個用戶的crontab -e : 編輯 crontab 的工作內(nèi)容 -l : 查閱 crontab 的工作內(nèi)容 -r : 移除所有的 crontab 的工作內(nèi)容
配置文件示例:
* * * * * touch ~/crontab_test * 3 * * * ~/backup 0 */2 * * * /sbin/service httpd restart
實現(xiàn)原理
crond 守護進(jìn)程是通過 Linux 啟動時的 init 進(jìn)程啟動,由 cornd 每分鐘會檢查/etc/crontab 配置文件中是否有需要執(zhí)行的任務(wù),并通過 /var/log/cron 文件輸出定時任務(wù)的執(zhí)行情況。用戶可以使用 Crontab 命令管理/etc/crontab 配置文件。
方案分析
借助 Crontab 用戶可以十分便利的快速實現(xiàn)簡易的定時任務(wù)功能,但存在以下痛點:
- 定時任務(wù)與指定 linux 機器綁定,當(dāng)機器擴容或者更換時需要重新配置 contab,同時存在單點故障風(fēng)險
- 隨著定時任務(wù)規(guī)模增多,無統(tǒng)一視角對其進(jìn)行任務(wù)進(jìn)度的追蹤和管控,難以維護
- 功能過于簡單,沒有超時,重試,阻塞等任務(wù)高級特性
- 可觀測能力差,問題排查定位困難
- 任務(wù)常駐,當(dāng)無任務(wù)執(zhí)行時造成不必要的資源成本浪費
Spring Task
目標(biāo)定位
Spring 框架提供了開箱即用的定時調(diào)度功能,用戶可以通過 xml 或者@Scheduled 注解的方式標(biāo)識指定方法執(zhí)行的周期。Spring Task 支持多種任務(wù)執(zhí)行模式,包括帶時區(qū)配置的 corn,固定延遲,固定速率等。
使用方式
代碼實例如下:
@EnableScheduling
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
@Component
public class MyTask {
@Scheduled(cron = "0 0 1 * * *")
public void test() {
System.out.println("test");
}
}
實現(xiàn)原理
Spring Task 的原理是在初始化 bean 時借助 ScheduledAnnotationBeanPostProcessor 攔截@Scheduled 注解所標(biāo)識的方法,并根據(jù)每個方法及其注解配置構(gòu)建相應(yīng)的 Task 實例注冊到 ScheduledTaskRegistrar 中,并在單例 bean 初始化完成后通過 afterSingletonsInstantiated 回調(diào)設(shè)置 ScheduledTaskRegistrar 中的調(diào)度器 TaskScheduler,其底層依賴于 jdk 并發(fā)包中的 ScheduledThreadPoolExecutor 實現(xiàn),并在 afterPropertiesSet 時將所有 Task 添加到 TaskScheduler 中調(diào)度執(zhí)行。
方案分析
借助 Spring Task 用戶可以通過注解快速實現(xiàn)對指定方法的周期性執(zhí)行,支持多種周期性策略。但與 crontab 相似,同樣有如下的痛點:
- 默認(rèn)為單線程執(zhí)行,若前一個任務(wù)執(zhí)行時間較長會導(dǎo)致后續(xù)任饑餓阻塞,需要用戶自行配置線程池
- 各個節(jié)點獨立運行,存在單點風(fēng)險,無分布式協(xié)調(diào)機制,要考慮禁止并發(fā)執(zhí)行
- 隨著定時任務(wù)規(guī)模增多,無統(tǒng)一視角對其進(jìn)行任務(wù)進(jìn)度的追蹤和管控,難以維護
- 功能過于簡單,沒有超時,重試,阻塞等任務(wù)高級特性
- 可觀測能力差,問題排查定位困難
- 任務(wù)常駐,當(dāng)無任務(wù)執(zhí)行時造成不必要的資源成本浪費
ElasticJob
目標(biāo)定位
ElasticJob 作為當(dāng)當(dāng)網(wǎng)開源的一款分布式任務(wù)框架,提供彈性調(diào)度,資源管控,作業(yè)治理等諸多特性,其已經(jīng)成為 Apache Shardingsphere 的子項目。ElasticJob 目前由 2 個相互獨立的子項目 ElasticJob-Lite 和 ElasticJob-Cloud 組成,ElasticJob-Lite 定位為輕量級無中心化解決方案,使用 jar 的形式提供分布式任務(wù)的協(xié)調(diào)服務(wù);ElasticJob-Cloud 使用 Mesos 的解決方案,額外提供資源治理、應(yīng)用分發(fā)以及進(jìn)程隔離等服務(wù)。一般使用 ElasticJob-Lite 即可滿足需求。
使用方式
使用者需要在 yaml 中配置注冊中心 zk 的地址以及任務(wù)的配置信息:
elasticjob:
regCenter:
serverLists: localhost:6181
namespace: elasticjob-lite-springboot
jobs:
simpleJob:
elasticJobClass: org.apache.shardingsphere.elasticjob.lite.example.job.SpringBootSimpleJob
cron: 0/5 * * * * ?
timeZone: GMT+08:00
shardingTotalCount: 3
shardingItemParameters: 0=Beijing,1=Shanghai,2=Guangzhou
實現(xiàn)對應(yīng)的接口即可標(biāo)識對應(yīng)的任務(wù),同時通過配置監(jiān)聽器來實現(xiàn)任務(wù)執(zhí)行前后回調(diào):
public class MyElasticJob implements SimpleJob {
@Override
public void execute(ShardingContext context) {
switch (context.getShardingItem()) {
case 0:
// do something by sharding item 0
break;
case 1:
// do something by sharding item 1
break;
case 2:
// do something by sharding item 2
break;
// case n: ...
}
}
}
public class MyJobListener implements ElasticJobListener {
@Override
public void beforeJobExecuted(ShardingContexts shardingContexts) {
// do something ...
}
@Override
public void afterJobExecuted(ShardingContexts shardingContexts) {
// do something ...
}
@Override
public String getType() {
return "simpleJobListener";
}
}
實現(xiàn)原理
ElasticJob 底層時間調(diào)度基于 Quartz,Quartz 需要持久化業(yè)務(wù) Bean 到底層數(shù)據(jù)表中,系統(tǒng)侵入性相當(dāng)嚴(yán)重,同時通過 db 鎖進(jìn)行任務(wù)搶占,不支持并行調(diào)度,不具備可擴展性。而 ElasticJob 通過數(shù)據(jù)分片以及自定義分片參數(shù)的特性完成了水平擴展,可以將一個任務(wù)拆分為 N 個獨立的任務(wù)項,由分布式的服務(wù)器并行執(zhí)行各自分配到的分片項。比如一個數(shù)據(jù)庫中有 1 億條數(shù)據(jù),需要將這些數(shù)據(jù)讀取出來并進(jìn)行計算,就可以將這 1 億條數(shù)據(jù)劃分成 10 個分片,每一個分片讀取其中的 1 千萬條數(shù)據(jù),然后計算后寫入數(shù)據(jù)庫。如果有三臺機器執(zhí)行,A 機器分到分片(0,1,2,9),B 機器分到分片(3,4,5),C 機器分到分片(6,7,8),這也是相比于 Quartz 最顯著的優(yōu)勢。
實現(xiàn)上 ElasticJob 使用 zookeeper 作為注冊中心進(jìn)行分布式調(diào)度協(xié)調(diào)以及 leader 節(jié)點的選舉,通過注冊中心的臨時節(jié)點的變化來感知服務(wù)器的增減,每當(dāng) leader 節(jié)點選舉,服務(wù)器上下線,分片總數(shù)變更時均會觸發(fā)后續(xù)的重新分片,由 leader 節(jié)點在下次定時任務(wù)觸發(fā)時進(jìn)行具體的分片劃分,再由各節(jié)點從注冊中心中獲取分片信息,作為任務(wù)的運行參數(shù)進(jìn)行執(zhí)行。

方案分析
ElasticJob 作為分布式任務(wù)框架,解決了上述單節(jié)點任務(wù)無法保證任務(wù)執(zhí)行過程中的高可用和高并發(fā)下執(zhí)行的性能的問題,并支持失敗轉(zhuǎn)移(failover)和錯過執(zhí)行的作業(yè)重新執(zhí)行(misfire)等高級機制,但在使用過程中仍存在以下痛點:
- 框架整體較重,需要依賴外置注冊中心zk,增加了至少三臺服務(wù)器的使用成本和維護復(fù)雜度
- 隨著任務(wù)量的不斷增多,zk 作為有狀態(tài)中間件將會成為性能瓶頸
- 可觀測能力弱,需要額外引入 db 并配置事件追蹤
- 任務(wù)常駐,當(dāng)無任務(wù)執(zhí)行時造成不必要的資源成本浪費
XXLJob
目標(biāo)定位
XXLJob 作為大眾點評員工開源的一款分布式任務(wù)框架,其核心設(shè)計目標(biāo)是開發(fā)迅速、學(xué)習(xí)簡單、輕量級、易擴展。XXLJob 具備豐富的功能,如任務(wù)分片廣播,超時控制,失敗重試,阻塞策略等,并通過體驗友好的白屏化控制臺對任務(wù)進(jìn)行管理和維護。
使用方式
XXLJob 分為中心式調(diào)度器和分布式執(zhí)行器兩部分組成,在使用時需要分別啟動,在調(diào)度中心啟動時需要配置所依賴的 db 配置。
執(zhí)行器需要配置調(diào)度中心的地址:
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin xxl.job.accessToken= xxl.job.executor.appname=xxl-job-executor-sample xxl.job.executor.address= xxl.job.executor.ip= xxl.job.executor.port=9999 xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler xxl.job.executor.logretentiondays=30
通過 bean 模式方法形式創(chuàng)建任務(wù)和前后回調(diào)的使用方式如下:
@XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy")
public void demoJobHandler() throws Exception {
int shardIndex = XxlJobHelper.getShardIndex();
int shardTotal = XxlJobHelper.getShardTotal();
XxlJobHelper.log("分片參數(shù):當(dāng)前分片序號 = {}, 總分片數(shù) = {}", shardIndex, shardTotal);
}
public void init(){
logger.info("init");
}
public void destroy(){
logger.info("destory");
}
創(chuàng)建任務(wù)完成后,需要在控制臺上配置任務(wù)的執(zhí)行策略:

實現(xiàn)原理
XXLJob 實現(xiàn)上將調(diào)度系統(tǒng)與任務(wù)解耦,其自研調(diào)度器負(fù)責(zé)管理調(diào)度信息,按照調(diào)度配置發(fā)出調(diào)度請求,支持可視化、簡單且動態(tài)的管理調(diào)度信息,自動發(fā)現(xiàn)和路由,調(diào)度過期策略,重試策略,支持執(zhí)行器 Failover。執(zhí)行器負(fù)責(zé)接收調(diào)度請求并執(zhí)行任務(wù)邏輯,并接收任務(wù)終止請求和日志請求,負(fù)責(zé)任務(wù)超時,阻塞策略等。調(diào)度器和執(zhí)行器通過 restful api 進(jìn)行通信,調(diào)度器本身無狀態(tài)支持集群部署,提升調(diào)度系統(tǒng)容災(zāi)和可用性,通過 mysql 維護鎖信息和持久化。執(zhí)行器無狀態(tài)支持集群部署,提升調(diào)度系統(tǒng)可用性,同時提升任務(wù)處理能力。
XXLJob 一次完整的任務(wù)調(diào)度通訊流程:首先調(diào)度中心向執(zhí)行器內(nèi)嵌 Server 發(fā)送 http 調(diào)度請求,然后執(zhí)行器執(zhí)行對應(yīng)的任務(wù)邏輯,待任務(wù)執(zhí)行完成或超時后執(zhí)行器發(fā)送 http 回調(diào)向調(diào)度中心返回調(diào)度結(jié)果。

方案分析
XXLJob 在開源社區(qū)廣泛流行,憑借其簡單的操作和豐富的功能已在多家公司投入使用,可以較好的滿足生產(chǎn)級別的需求,但下面的一些痛點需要改進(jìn):
- 需要依賴外置 DB,增加了數(shù)據(jù)庫的使用成本和維護復(fù)雜度
- 依賴 DB 鎖保證集群分布式調(diào)度的一致性,當(dāng)短時任務(wù)量不斷增多將對 db 造成較大壓力,成為性能瓶頸
- 相較于無中心模式需要額外部署調(diào)度器,調(diào)度器和執(zhí)行器均需要常駐同時為保證高可用均至少兩臺,當(dāng)無任務(wù)執(zhí)行時造成不必要的資源成本浪費
Serverless Job
目標(biāo)定位
Serverless 作為云計算的最佳實踐和未來演進(jìn)趨勢,其全托管免運維的使用體驗和按量付費的成本優(yōu)勢使得其在云原生時代備受推崇。SAE (Serverless 應(yīng)用引擎)Job 作為首款面向任務(wù)的 Serverless PaaS 平臺,提供傳統(tǒng)的用戶體驗。當(dāng)前聚焦支持單機、廣播、并行分片模型的任務(wù),同時支持事件驅(qū)動、并發(fā)策略和超時重試等諸多特性,提供低成本、多規(guī)格、高彈性的資源實例來滿足短時任務(wù)的執(zhí)行。

使用方式
對于使用上述所有方案的存量應(yīng)用,SAE (Serverless 應(yīng)用引擎) Job 在兼容功能體驗的同時支持零改造無感遷移,無論用戶使用的是 Crontab,Spring Task,還是 ElasticJob, XXLJob,均可將代碼包或者鏡像直接部署到 SAE (Serverless 應(yīng)用引擎) Job中,直接升級成為 Serverless 架構(gòu), 從而即刻擁有 Serverless 所帶來的技術(shù)上的優(yōu)勢,節(jié)省資源成本和運維成本。
對于運完即停的程序,無論是 Java,還是 Shell,Python,Go,Php 均可以直接部署到 SAE (Serverless 應(yīng)用引擎) Job 中, 從而即刻擁有白屏化管控,全托管免運維的完備體驗以及開箱即用的可觀測功能。
實現(xiàn)原理
SAE (Serverless 應(yīng)用引擎)Job 底層為多租 Kubernetes,使用神龍裸金屬安全容器、VK 對接 ECI 兩種方式提供集群計算資源。用戶在 SAE(Serverless 應(yīng)用引擎)中運行的任務(wù)會映射到 Kubernetes 中相應(yīng)的資源。其中多租能力是借助系統(tǒng)隔離、數(shù)據(jù)隔離、服務(wù)隔離和網(wǎng)絡(luò)隔離實現(xiàn)租戶間的隔離。SAE (Serverless 應(yīng)用引擎)Job 通過 Event Bridge 作為事件驅(qū)動源,在支持豐富調(diào)用方式的同時避免了 Kubernetes 內(nèi)置定時器不保證準(zhǔn)時觸發(fā)以及精度時區(qū)上的問題。同時不斷完善 Job 控制器的企業(yè)級特性,新增自定義分片,注入配置,差異化 GC,sidecar 主動退出,實時日志持久化,事件通知等機制。并借助 Java 字節(jié)碼增強技術(shù)接管任務(wù)調(diào)度,實現(xiàn)通用的 Java 目標(biāo)框架的零改造 Serverless 化。使用 KubeVela 軟件交付平臺作為任務(wù)發(fā)布和管理的載體,以任務(wù)為中心,以開源開放的標(biāo)準(zhǔn),通過聲明式的方式完成整個云原生交付。SAE (Serverless 應(yīng)用引擎)Job 將持續(xù)優(yōu)化 etcd 以及調(diào)度器在短時任務(wù)高并發(fā)創(chuàng)建場景下的效率以及實例啟動的極致彈性能力,結(jié)合彈性資源池保證任務(wù)調(diào)度的低延遲和高可用。
方案分析
SAE (Serverless 應(yīng)用引擎)Job 解決了上述定時任務(wù)解決方案的各種痛點,用戶無需關(guān)注任務(wù)的調(diào)度和集群資源,無需部署的額外的運維依賴組件,也無需自建一整套監(jiān)控告警系統(tǒng),同時更重要的是無需云主機 7*24h 常駐,在低資源利用率的環(huán)境下持續(xù)消耗閑置資源。
SAE (Serverless 應(yīng)用引擎)Job 相較于傳統(tǒng)定時任務(wù)解決方案提供了三大核心價值:
- 完備全托管:提供了一站式全托管的管理界面,其任務(wù)生命周期管理、可觀測性開箱即用,用戶可以低心智負(fù)擔(dān)、零成本地學(xué)習(xí)使用 SAE (Serverless 應(yīng)用引擎)。
- 簡單免運維:屏蔽了底層資源,用戶只需關(guān)注其核心的業(yè)務(wù)邏輯開發(fā),無需操心集群可用性、容量、性能等方面的問題。
- 超高性價比:采用按需使用、按量付費的模式,只有任務(wù)執(zhí)行業(yè)務(wù)邏輯時才會拉起收費,其余時間不收取任何費用,極大節(jié)省了資源的成本開銷。
總結(jié)
本文對主流定時任務(wù)解決方案(Crontab, Spring Task, ElasticJob, XXLJob, Serverless Job)的目標(biāo)定位、使用方式、實現(xiàn)原理進(jìn)行了闡述,同時就企業(yè)密切關(guān)注的功能體驗,性能成本方面的問題進(jìn)行橫評分析。最后期望大家選用 Serverless Job,感受其對傳統(tǒng)任務(wù)所帶來的新變革。
以上就是java開發(fā)主流定時任務(wù)解決方案全橫評詳解的詳細(xì)內(nèi)容,更多關(guān)于java 主流定時任務(wù)解決方案的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot中l(wèi)ogback日志保存到mongoDB的方法
這篇文章主要介紹了SpringBoot中l(wèi)ogback日志保存到mongoDB的方法,2017-11-11
SpringBoot如何配置獲取request中body的json格式參數(shù)
這篇文章主要介紹了SpringBoot如何配置獲取request中body的json格式參數(shù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
SpringCloud Feign遠(yuǎn)程調(diào)用實現(xiàn)詳解
Feign是Netflix公司開發(fā)的一個聲明式的REST調(diào)用客戶端; Ribbon負(fù)載均衡、 Hystrⅸ服務(wù)熔斷是我們Spring Cloud中進(jìn)行微服務(wù)開發(fā)非?;A(chǔ)的組件,在使用的過程中我們也發(fā)現(xiàn)它們一般都是同時出現(xiàn)的,而且配置也都非常相似2022-11-11
bool當(dāng)成函數(shù)參數(shù)錯誤理解
經(jīng)常會在函數(shù)的參數(shù)里使用bool參數(shù),這會大大地降低代碼的可讀性2012-11-11
詳解Java數(shù)組擴容縮容與拷貝的實現(xiàn)和原理
這篇文章主要帶大家學(xué)習(xí)數(shù)組的擴容、縮容及拷貝,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
Springboot整合Dubbo教程之項目創(chuàng)建和環(huán)境搭建
本篇文章主要介紹了Springboot整合Dubbo教程之項目創(chuàng)建和環(huán)境搭建,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12

