定時(shí)任務(wù)作為一種按照約定時(shí)間執(zhí)行預(yù)期邏輯的通用模式,在企業(yè)級(jí)開發(fā)中承載著豐富的業(yè)務(wù)場(chǎng)景,諸如后臺(tái)定時(shí)同步數(shù)據(jù)生成報(bào)表,定時(shí)清理磁盤日志文件,定時(shí)掃描超時(shí)訂單進(jìn)行補(bǔ)償回調(diào)等。程序開發(fā)人員在定時(shí)任務(wù)領(lǐng)域有著諸多框架和方案可供選擇,并借此快速實(shí)現(xiàn)業(yè)務(wù)功能實(shí)現(xiàn)產(chǎn)品上線。本文將就當(dāng)前主流定時(shí)任務(wù)解決方案進(jìn)行介紹和分析,期望可以在企業(yè)技術(shù)選型和項(xiàng)目架構(gòu)重構(gòu)時(shí)作為參考。
01
Crontab
Aliware
01
目標(biāo)定位
Crontab 作為 linux 內(nèi)置的可執(zhí)行命令,可以實(shí)現(xiàn)按照 cron 表達(dá)式生成的時(shí)間執(zhí)行指定的系統(tǒng)指令或 shell 腳本。
02
使用方式
crontab 命令語法:
crontab [-u username] [-l | -e | -r ]
參數(shù):
-u : 只有root用戶才能進(jìn)行這個(gè)任務(wù),編輯某個(gè)用戶的crontab
-e : 編輯 crontab 的工作內(nèi)容
-l : 查閱 crontab 的工作內(nèi)容
-r : 移除所有的 crontab 的工作內(nèi)容
配置文件示例:
* * * * * touch ~/crontab_test
* 3 * * * ~/backup
0 */2 * * * /sbin/service httpd restart
03
實(shí)現(xiàn)原理
crond 守護(hù)進(jìn)程是通過 Linux 啟動(dòng)時(shí)的 init 進(jìn)程啟動(dòng),由 cornd 每分鐘會(huì)檢查/etc/crontab 配置文件中是否有需要執(zhí)行的任務(wù),并通過 /var/log/cron 文件輸出定時(shí)任務(wù)的執(zhí)行情況。用戶可以使用 Crontab 命令管理/etc/crontab 配置文件。
04
方案分析
借助 Crontab 用戶可以十分便利地快速實(shí)現(xiàn)簡易的定時(shí)任務(wù)功能,但存在以下痛點(diǎn):
- 定時(shí)任務(wù)與指定 linux 機(jī)器綁定,當(dāng)機(jī)器擴(kuò)容或者更換時(shí)需要重新配置 contab,同時(shí)存在單點(diǎn)故障風(fēng)險(xiǎn)
- 隨著定時(shí)任務(wù)規(guī)模增多,無法統(tǒng)一視角對(duì)其進(jìn)行任務(wù)進(jìn)度的追蹤和管控,難以維護(hù)
- 功能過于簡單,沒有超時(shí),重試,阻塞等任務(wù)的高級(jí)特性
- 可觀測(cè)能力差,問題排查定位困難
- 任務(wù)常駐,當(dāng)無任務(wù)執(zhí)行時(shí)造成不必要的資源成本浪費(fèi)
02
Spring Task
Aliware
01
目標(biāo)定位
Spring 框架提供了開箱即用的定時(shí)調(diào)度功能,用戶可以通過 xml 或者@Scheduled 注解的方式標(biāo)識(shí)指定方法執(zhí)行的周期。Spring Task 支持多種任務(wù)執(zhí)行模式,包括帶時(shí)區(qū)配置的 corn,固定延遲,固定速率等。
02
使用方式
代碼實(shí)例如下:
@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");
}
}
03
實(shí)現(xiàn)原理
Spring Task 原理是在初始化 bean 時(shí)借助
ScheduledAnnotationBeanPostProcessor 攔截@Scheduled 注解所標(biāo)識(shí)的方法,并根據(jù)每個(gè)方法及其注解配置構(gòu)建相應(yīng)的 Task 實(shí)例注冊(cè)到 ScheduledTaskRegistrar 中,并在單例 bean 初始化完成后通過 afterSingletonsInstantiated 回調(diào)設(shè)置 ScheduledTaskRegistrar 中的調(diào)度器 TaskScheduler,其底層依賴于 jdk 并發(fā)包中的 ScheduledThreadPoolExecutor 實(shí)現(xiàn),并在 afterPropertiesSet 時(shí)將所有 Task 添加到 TaskScheduler 中調(diào)度執(zhí)行。
04
方案分析
借助 Spring Task 用戶可以通過注解快速實(shí)現(xiàn)對(duì)指定方法的周期性執(zhí)行,支持多種周期性策略。但與 crontab 相似,同樣有如下的痛點(diǎn):
- 默認(rèn)為單線程執(zhí)行,若前一個(gè)任務(wù)執(zhí)行時(shí)間較長會(huì)導(dǎo)致后續(xù)任務(wù)阻塞,需要用戶自行配置線程池
- 各個(gè)節(jié)點(diǎn)獨(dú)立運(yùn)行,存在單點(diǎn)風(fēng)險(xiǎn),無分布式協(xié)調(diào)機(jī)制,要考慮禁止并發(fā)執(zhí)行
- 隨著定時(shí)任務(wù)規(guī)模增多,無法統(tǒng)一視角對(duì)其進(jìn)行任務(wù)進(jìn)度的追蹤和管控,難以維護(hù)
- 功能過于簡單,沒有超時(shí),重試,阻塞等任務(wù)的高級(jí)特性
- 可觀測(cè)能力差,問題排查定位困難
- 任務(wù)常駐,當(dāng)無任務(wù)執(zhí)行時(shí)造成不必要的資源成本浪費(fèi)
03
ElasticJob
Aliware
01
目標(biāo)定位
ElasticJob 作為當(dāng)當(dāng)網(wǎng)開源的一款分布式任務(wù)框架,提供彈性調(diào)度,資源管控,作業(yè)治理等諸多特性,其已經(jīng)成為 Apache Shardingsphere 的子項(xiàng)目。ElasticJob 目前由 2 相互獨(dú)立的子項(xiàng)目 ElasticJob-Lite 和 ElasticJob-Cloud 組成,ElasticJob-Lite 定位為輕量級(jí)無中心化解決方案,使用 jar 的形式提供分布式任務(wù)的協(xié)調(diào)服務(wù);ElasticJob-Cloud 使用 Mesos 的解決方案,額外提供資源治理、應(yīng)用分發(fā)以及進(jìn)程隔離等服務(wù)。一般使用 ElasticJob-Lite 即可滿足需求。
02
使用方式
使用者需要在 yaml 中配置注冊(cè)中心 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
實(shí)現(xiàn)對(duì)應(yīng)的接口即可標(biāo)識(shí)對(duì)應(yīng)的任務(wù),同時(shí)通過配置監(jiān)聽器來實(shí)現(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";
}
}
03
實(shí)現(xiàn)原理
ElasticJob 底層時(shí)間調(diào)度基于 Quartz,Quartz 需要持久化業(yè)務(wù) Bean 到底層數(shù)據(jù)表中,系統(tǒng)侵入性相當(dāng)嚴(yán)重,同時(shí)通過 db 鎖定進(jìn)行任務(wù)搶占,不支持并行調(diào)度,不具備可擴(kuò)展性。而 ElasticJob 通過數(shù)據(jù)分片以及自定義分片參數(shù)的特性完成了水平擴(kuò)展,可以將一個(gè)任務(wù)拆分為 N 獨(dú)立的任務(wù)項(xiàng),由分布式的服務(wù)器并行執(zhí)行各自分配到的分片項(xiàng)。比如一個(gè)數(shù)據(jù)庫中有 1 億條數(shù)據(jù),需要將這些數(shù)據(jù)讀取出來并進(jìn)行計(jì)算,就可以將這 1 億條數(shù)據(jù)劃分成 10 個(gè)分片,每一個(gè)分片讀取其中的 1 千萬條數(shù)據(jù),然后計(jì)算后寫入數(shù)據(jù)庫。如果有三臺(tái)機(jī)器執(zhí)行,A 機(jī)器分到分片(0,1,2,9),B 機(jī)器分到分片(3,4,5),C 機(jī)器分到分片(6,7,8),這也是相比于 Quartz 最顯著的優(yōu)勢(shì)。
實(shí)現(xiàn)上 ElasticJob 使用 zookeeper 作為注冊(cè)中心進(jìn)行分布式調(diào)度協(xié)調(diào)以及 leader 節(jié)點(diǎn)的選舉,通過注冊(cè)中心的臨時(shí)節(jié)點(diǎn)的變化來感知服務(wù)器的增減,每當(dāng) leader 節(jié)點(diǎn)選舉,服務(wù)器上下線,分片總數(shù)變更時(shí)均會(huì)觸發(fā)后續(xù)的重新分片,由 leader 節(jié)點(diǎn)在下次定時(shí)任務(wù)觸發(fā)時(shí)進(jìn)行具體的分片劃分,再由各節(jié)點(diǎn)從注冊(cè)中心中獲取分片信息,作為任務(wù)的運(yùn)行參數(shù)進(jìn)行執(zhí)行。
04
方案分析
ElasticJob 作為分布式任務(wù)框架,解決了上述單節(jié)點(diǎn)任務(wù)無法保證任務(wù)執(zhí)行過程中的高可用和高并發(fā)下執(zhí)行的性能的問題,并支持失敗轉(zhuǎn)移(failover)和錯(cuò)過執(zhí)行的作業(yè)重新執(zhí)行(misfire)等高級(jí)機(jī)制,但在使用過程中仍存在以下痛點(diǎn):
- 框架整體較重,需要依賴外置注冊(cè)中心zk,增加了至少三臺(tái)服務(wù)器的使用成本和維護(hù)復(fù)雜度
- 隨著任務(wù)量的不斷增多,zk 作為有狀態(tài)中間件將會(huì)成為性能瓶頸
- 可觀測(cè)能力弱,需要額外引入 db 并配置事件追蹤
- 任務(wù)常駐,當(dāng)無任務(wù)執(zhí)行時(shí)造成不必要的資源成本浪費(fèi)
04
XXLJob
Aliware
01
目標(biāo)定位
XXLJob 作為大眾點(diǎn)評(píng)員工開源的一款分布式任務(wù)框架,其核心設(shè)計(jì)目標(biāo)是開發(fā)迅速、學(xué)習(xí)簡單、輕量級(jí)、易擴(kuò)展。XXLJob 具備豐富的功能,如任務(wù)分片廣播,超時(shí)控制,失敗重試,阻塞策略等,并通過體驗(yàn)友好的白屏化控制臺(tái)對(duì)任務(wù)進(jìn)行管理和維護(hù)。
02
使用方式
XXLJob 分為中心式調(diào)度器和分布式執(zhí)行器兩部分組成,在使用時(shí)需要分別啟動(dòng),在調(diào)度中心啟動(dòng)時(shí)需要配置所依賴的 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)前分片序號(hào) = {}, 總分片數(shù) = {}", shardIndex, shardTotal);
}
public void init(){
logger.info("init");
}
public void destroy(){
logger.info("destory");
}
創(chuàng)建任務(wù)完成后,需要在控制臺(tái)上配置任務(wù)的執(zhí)行策略:
03
實(shí)現(xiàn)原理
XXLJob 實(shí)現(xiàn)上將調(diào)度系統(tǒng)與任務(wù)解耦,其自研調(diào)度器負(fù)責(zé)管理調(diào)度信息,按照調(diào)度配置發(fā)出調(diào)度請(qǐng)求,支持可視化、簡單且動(dòng)態(tài)的管理調(diào)度信息,自動(dòng)發(fā)現(xiàn)和路由,調(diào)度過期策略,重試策略,支持執(zhí)行器 Failover。執(zhí)行器負(fù)責(zé)接收調(diào)度請(qǐng)求并執(zhí)行任務(wù)邏輯,并接收任務(wù)終止請(qǐng)求和日志請(qǐng)求,負(fù)責(zé)任務(wù)超時(shí),阻塞策略等。調(diào)度器和執(zhí)行器通過 restful api 進(jìn)行通信,調(diào)度器本身無狀態(tài)支持集群部署,提升調(diào)度系統(tǒng)容災(zāi)和可用性,通過 MySQL 維護(hù)鎖信息和持久化。執(zhí)行器無狀態(tài)支持集群部署,提升調(diào)度系統(tǒng)可用性,同時(shí)提升任務(wù)處理能力。
XXLJob 一次完整的任務(wù)調(diào)度通訊流程:首先調(diào)度中心向執(zhí)行器內(nèi)嵌 Server 發(fā)送 http 調(diào)度請(qǐng)求,然后執(zhí)行器執(zhí)行對(duì)應(yīng)的任務(wù)邏輯,待任務(wù)執(zhí)行完成或超時(shí)后執(zhí)行器發(fā)送 http 回調(diào)向調(diào)度中心返回調(diào)度結(jié)果。
04
方案分析
XXLJob 在開源社區(qū)廣泛流行,憑借其簡單的操作和豐富的功能已在多家公司投入使用,可以較好的滿足生產(chǎn)級(jí)別的需求,但下面的一些痛點(diǎn)需要改進(jìn):
- 需要依賴外置 DB,增加了數(shù)據(jù)庫的使用成本和維護(hù)復(fù)雜度
- 依賴 DB 鎖保證集群分布式調(diào)度的一致性,當(dāng)短時(shí)任務(wù)量不斷增多將對(duì) db 造成較大壓力,成為性能瓶頸
- 相較于無中心模式需要額外部署調(diào)度器,調(diào)度器和執(zhí)行器均需要常駐同時(shí)為保證高可用均至少兩臺(tái),當(dāng)無任務(wù)執(zhí)行時(shí)造成不必要的資源成本浪費(fèi)
05
Serverless Job
Aliware
01
目標(biāo)定位
Serverless 作為云計(jì)算的最佳實(shí)踐和未來演進(jìn)趨勢(shì),其全托管免運(yùn)維的使用體驗(yàn)和按量付費(fèi)的成本優(yōu)勢(shì)使得其在云原生時(shí)代備受推崇。SAE (Serverless 應(yīng)用引擎)Job 作為首款面向任務(wù)的 Serverless PaaS 平臺(tái),提供傳統(tǒng)的用戶體驗(yàn)。當(dāng)前聚焦支持單機(jī)、廣播、并行分片模型的任務(wù),同時(shí)支持事件驅(qū)動(dòng)、并發(fā)策略和超時(shí)重試等諸多特性,提供低成本、多規(guī)格、高彈性的資源實(shí)例來滿足短時(shí)任務(wù)的執(zhí)行。
02
使用方式
對(duì)于使用上述所有方案的存量應(yīng)用,SAE (Serverless 應(yīng)用引擎) Job 在兼容功能體驗(yàn)的同時(shí)支持零改造無感遷移,無論用戶使用的是 Crontab,Spring Task,還是 ElasticJob, XXLJob,均可將代碼包或者鏡像直接部署到 SAE (Serverless 應(yīng)用引擎) Job中,直接升級(jí)成為 Serverless 架構(gòu), 從而即刻擁有 Serverless 所帶來的技術(shù)上的優(yōu)勢(shì),節(jié)省資源成本和運(yùn)維成本。
對(duì)于運(yùn)完即停的程序,無論是 JAVA,還是 Shell,Python/ target=_blank class=infotextkey>Python,Go,php 均可以直接部署到 SAE (Serverless 應(yīng)用引擎) Job 中, 從而即刻擁有白屏化管控,全托管免運(yùn)維的完備體驗(yàn)以及開箱即用的可觀測(cè)功能。
03
實(shí)現(xiàn)原理
SAE (Serverless 應(yīng)用引擎)Job 底層為多租 Kube.NETes,使用神龍裸金屬安全容器、VK 對(duì)接 ECI 兩種方式提供集群計(jì)算資源。用戶在 SAE(Serverless 應(yīng)用引擎)中運(yùn)行的任務(wù)會(huì)映射到 Kubernetes 中相應(yīng)的資源。其中多租能力是借助系統(tǒng)隔離、數(shù)據(jù)隔離、服務(wù)隔離和網(wǎng)絡(luò)隔離實(shí)現(xiàn)租戶間的隔離。SAE (Serverless 應(yīng)用引擎)Job 通過 Event Bridge 作為事件驅(qū)動(dòng)源,在支持豐富調(diào)用方式的同時(shí)避免了 Kubernetes 內(nèi)置定時(shí)器不保證準(zhǔn)時(shí)觸發(fā)以及精度時(shí)區(qū)上的問題。同時(shí)不斷完善 Job 控制器的企業(yè)級(jí)特性,新增自定義分片,注入配置,差異化 GC,sidecar 主動(dòng)退出,實(shí)時(shí)日志持久化,事件通知等機(jī)制。并借助 Java 字節(jié)碼增強(qiáng)技術(shù)接管任務(wù)調(diào)度,實(shí)現(xiàn)通用的 Java 目標(biāo)框架的零改造 Serverless 化。使用 KubeVela 軟件交付平臺(tái)作為任務(wù)發(fā)布和管理的載體,以任務(wù)為中心,以開源開放的標(biāo)準(zhǔn),通過聲明式的方式完成整個(gè)云原生交付。SAE (Serverless 應(yīng)用引擎)Job 將持續(xù)優(yōu)化 etcd 以及調(diào)度器在短時(shí)任務(wù)高并發(fā)創(chuàng)建場(chǎng)景下的效率以及實(shí)例啟動(dòng)的極致彈性能力,結(jié)合彈性資源池保證任務(wù)調(diào)度的低延遲和高可用。
04
方案分析
SAE (Serverless 應(yīng)用引擎)Job 解決了上述定時(shí)任務(wù)解決方案的各種痛點(diǎn),用戶無需關(guān)注任務(wù)的調(diào)度和集群資源,無需部署的額外的運(yùn)維依賴組件,也無需自建一整套監(jiān)控告警系統(tǒng),同時(shí)更重要的是無需云主機(jī) 7*24h 常駐,在低資源利用率的環(huán)境下持續(xù)消耗閑置資源。
SAE (Serverless 應(yīng)用引擎)Job 相較于傳統(tǒng)定時(shí)任務(wù)解決方案提供了三大核心價(jià)值:
- 完備全托管:提供了一站式全托管的管理界面,其任務(wù)生命周期管理、可觀測(cè)性開箱即用,用戶可以低心智負(fù)擔(dān)、零成本地學(xué)習(xí)使用 SAE (Serverless 應(yīng)用引擎)。
- 簡單免運(yùn)維:屏蔽了底層資源,用戶只需關(guān)注其核心的業(yè)務(wù)邏輯開發(fā),無需操心集群可用性、容量、性能等方面的問題。
- 超高性價(jià)比:采用按需使用、按量付費(fèi)的模式,只有任務(wù)執(zhí)行業(yè)務(wù)邏輯時(shí)才會(huì)拉起收費(fèi),其余時(shí)間不收取任何費(fèi)用,極大節(jié)省了資源的成本開銷。
06
總結(jié)
Aliware
本文對(duì)主流定時(shí)任務(wù)解決方案(Crontab, Spring Task, ElasticJob, XXLJob, Serverless Job)的目標(biāo)定位、使用方式、實(shí)現(xiàn)原理進(jìn)行了闡述,同時(shí)就企業(yè)密切關(guān)注的功能體驗(yàn),性能成本方面的問題進(jìn)行橫評(píng)分析。最后期望大家選用 Serverless Job,感受其對(duì)傳統(tǒng)任務(wù)所帶來的新變革。
點(diǎn)擊下方了解更多 SAE Job 的功能優(yōu)勢(shì),和眾多開源任務(wù)框架“低門檻”遷移的方案!