分佈式定時任務調度框架選型

背景

業務場景

  1. 定期執行任務:如每天 0 點做資源稽查;

需求和痛點

  1. 集羣部署服務時,如何確保任務不被重複執行?--- 最急迫

  2. 如何監控、告警等;

  3. 高可用、無單點故障;

  4. 優秀的並行處理能力、分片能力;

自研 or 開源

任何工具的使用都要結合自身的業務場景,脫落業務場景談技術選型就是耍流氓。
考慮私有云場景業務量一般,高併發場景很少遇到,同一時間也不會有超大量定時任務同時需要執行,所以考慮自研也未嘗不可。
目前自研最急需解決的問題並不是高併發,而是如何避免任務被重複執行;
場景就變成了:

多個應用在同一個時間都嘗試去執行任務,但最終只有一個應用真正執行。

這樣的話,立馬就會讓人聯想到使用去解決,因爲是多個應用,所以就是分佈式鎖。那麼,場景又變了:

多個應用在同一個時間都嘗試去獲取分佈式鎖,只有一個應用能搶到這把鎖,搶到鎖的應用可以執行定時任務,其他應用則直接放棄,等待下一次執行時間。

搶鎖的時機是每次定時任務執行之前,這又讓我聯想到了AOP,那麼利用註解也就順理成章了。

下面的 ShedLock就是基於 AOP + 註解的思想。

ShedLock

與 Spring 的集成挺方便;

Distributed lock for your scheduled tasks

Github: start: 1K  Fork: 192,最近一次代碼提交 6months ago

ShedLock makes sure that your scheduled tasks are executed at most once at the same time. If a task is being executed on one node, it acquires a lock which prevents execution of the same task from another node (or thread). Please note, that if one task is already being executed on one node, execution on other nodes does not wait, it is simply skipped.

ShedLock uses external store like Mongo, JDBC database, Redis, Hazelcast, ZooKeeper or others for coordination.

ShedLock is not a distributed scheduler

Please note that ShedLock is not and will never be full-fledged scheduler, it's just a lock. If you need a distributed scheduler, please use another project. ShedLock is designed to be used in situations where you have scheduled tasks that are not ready to be executed in parallel, but can be safely executed repeatedly.

重要信息:

  1. ShedLock 可使用 MongoDB、JDBC-DB、Redis 或 Zookeeper 等來實現分佈式鎖,具體採用哪種方式,由使用者決定;

  2. 它僅僅是一個分佈式鎖,並不是調度程序;

與 Spring 的集成很簡單(官方文檔的示例,自己並未測試過),示例:

假設一個 task 每 15 分鐘執行一次,每次運行的時間都不是很長(即:應該在 15min 分鐘內可以運行完),更重要的是,不管起多少個實例,都只希望在 15 分鐘內該任務有且只被運行一次

//示例:與Spring的原生註解 @Scheduled配合使用
import net.javacrumbs.shedlock.core.SchedulerLock;

@Scheduled(cron = "0 */15 * * * *")//每15分鐘運行一次
@SchedulerLock(name = "scheduledTaskName", lockAtMostFor = "14m", lockAtLeastFor = "14m")
public void scheduledTask() {
   // do something
}
//lockAtMostFor : 表示最多鎖定14分鐘,主要用於防止執行任務的節點掛掉(即使這個節點掛掉,在14分鐘後,鎖也被釋放),一般將其設置爲明顯大於任務的最大執行時長;如果任務運行時間超過該值(即任務14分鐘沒有執行完),則該任務可能被重複執行!
//lockAtLeastFor : 至少鎖定時長,確保在15分鐘內,該任務不會運行超過1次;

額外補充一句,關於 @Scheduled的一些不注意的細節(單線程執行),需要特別注意:SpringTask 實現定時任務

Configure LockProvider

使用 JdbcTemplete 來實現分佈式鎖

CREATE TABLE shedlock(
    name VARCHAR(64),
    lock_until TIMESTAMP(3) NULL,
    locked_at TIMESTAMP(3) NULL,
    locked_by  VARCHAR(255),
    PRIMARY KEY (name)
)

依賴

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jdbc-template</artifactId>
    <version>4.5.1</version>
</dependency>

配置

import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;



...




@Bean
public LockProvider lockProvider(DataSource dataSource) {
    return new JdbcTemplateLockProvider(dataSource);
}

@Bean
    public ScheduledLockConfiguration scheduledLockConfiguration(LockProvider lockProvider) {
        return ScheduledLockConfigurationBuilder
                .withLockProvider(lockProvider)
                .withPoolSize(10)
                .withDefaultLockAtMostFor(Duration.ofMinutes(10))
                .build();
    }

基於 Piston 的思路(異步監控),重寫一個

todo

思路:

  1. 還是基於 Innodb 引擎的行鎖;

  2. 狀態:應該基於調度時間去判斷狀態;

選型參考

  1. 簡單、易上手;

  2. 額外依賴較少,比如是否需要依賴 MQ、Zookeeper 等;

  3. 是否方便集成;

開源產品對比

開源產品:任務調度系統彙總

Quartz

Java 事實上的定時任務標準。但 Quartz 關注點在於定時任務而非數據,並無一套根據數據處理而定製化的流程。雖然 Quartz 可以基於數據庫實現作業的高可用,但缺少分佈式並行調度的功能

原理

獨立的 Quratz 節點之間是不需要通信的,不同節點之間是通過數據庫表來感知另一個應用,只有使用持久的 JobStore 才能完成 Quartz 集羣。如果某一個節點失效,那麼 Job 會在其他節點上執行。

如何保證只在一臺機器上觸發?

缺點

  1. 不適合大量的短任務 & 不適合過多節點部署;

  2. 解決了高可用的問題,並沒有解決任務分片的問題,存在單機處理的極限(即:不能實現水平擴展)。

  3. 需要把任務信息持久化到業務數據表,和業務有耦合

  4. 調度邏輯和執行邏輯並存於同一個項目中,在機器性能固定的情況下,業務和調度之間不可避免地會相互影響。

  5. quartz 集羣模式下,是通過數據庫獨佔鎖來唯一獲取任務,任務執行並沒有實現完善的負載均衡機制。

Elastic-Job

官方文檔

GitHub: star 5.7K Fork:2.6K

Elastic-Job is a distributed scheduled job framework, based on Quartz and Zookeeper

Elastic Job 是噹噹網架構師開發,是一個分佈式調度解決方案,由兩個相互獨立的子項目 Elastic-Job-Lite 和 Elastic-Job-Cloud 組成;定位爲輕量級無中心化解決方案,使用 jar 包的形式提供分佈式任務的協調服務。支持分佈式調度協調、彈性擴容縮容、失效轉移、錯過執行作業重觸發、並行調度、自診斷和修復等等功能特性。

Elastic-Job-Lite 定位爲輕量級無中心化解決方案,使用 jar 包的形式提供分佈式任務的協調服務;Elastic-Job-Cloud 採用自研 Mesos Framework 的解決方案,額外提供資源治理、應用分發以及進程隔離等功能;

Elastic-Job-Lite 並沒有宿主程序,而是基於部署作業框架的程序在到達相應時間點時各自觸發調度。它的開發也比較簡單,引用 Jar 包實現一些方法即可,最後編譯成 Jar 包運行。Elastic-Job-Lite 的分佈式部署全靠 ZooKeeper 來同步狀態和原數據。實現高可用的任務只需將分片總數設置爲 1,並把開發的 Jar 包部署於多個服務器上執行,任務將會以 1 主 N 從的方式執行。一旦本次執行任務的服務器崩潰,其他執行任務的服務器將會在下次作業啓動時選擇一個替補執行。如果開啓了失效轉移,那麼功能效果更好,可以保證在本次作業執行時崩潰,備機之一立即啓動替補執行。

Elastic-Job-Lite 的任務分片也是通過 ZooKeeper 來實現,Elastic-Job 並不直接提供數據處理的功能,框架只會將分片項分配至各個運行中的作業服務器,開發者需要自行處理分片項與真實數據的對應關係。框架也預置了一些分片策略:平均分配算法策略,作業名哈希值奇偶數算法策略,輪轉分片策略。同時也提供了自定義分片策略的接口。

另外 Elastic-Job-Lite 還提供了一個任務監控和管理界面:Elastic-Job-Lite-Console。它和 Elastic-Job-Lite 是兩個完全不關聯的應用程序,使用 ZooKeeper 來交換數據,管理人員可以通過這個界面查看、監控和管理 Elastic-Job-Lite 的任務,必要的時候還能手動觸發任務

功能列表

優缺點

優點:

缺點:

XXL-JOB

官方文檔

GitHub: star: 12.9K  Fork:5.5K

使用該框架的公司:>300 家

XXL-Job 官網是大衆點評員工徐雪裏於 2015 年發佈的分佈式任務調度平臺,其核心設計目標是開發迅速、學習簡單、輕量級、易擴展。現已開放源代碼並接入多家公司線上產品線,開箱即用。

設計思想

將調度行爲抽象形成 “調度中心” 公共平臺,而平臺自身並不承擔業務邏輯,“調度中心”負責發起調度請求。

將任務抽象成分散的 JobHandler,交由 “執行器” 統一管理,“執行器”負責接收調度請求並執行對應的 JobHandler 中業務邏輯。

因此,“調度”和 “任務” 兩部分可以相互解耦,提高系統整體穩定性和擴展性;

系統組成

其他

uncode-schedule

基於 zookeeper,比較小衆,不推薦

基於 zookeeper+spring task/quartz 的分佈式任務調度組件,確保所有任務在集羣中不重複,不遺漏的執行。支持動態添加和刪除任務。

LTS

最近一次更新在 2 年前,目前不是很活躍;也比較小衆,不推薦

light-task-scheduler

LTS(light-task-scheduler) 主要用於解決分佈式任務調度問題,支持實時任務,定時任務和 Cron 任務。有較好的伸縮性,擴展性;

TBSchedule

阿里早期開源的分佈式任務調度系統。代碼略陳舊,使用 timer 而非線程池執行任務調度。衆所周知,timer 在處理異常狀況時是有缺陷的。而且 TBSchedule 作業類型較爲單一,只能是獲取 / 處理數據一種模式。還有就是文檔缺失比較嚴重。

依賴 Zookeeper;

代碼太久沒更新

TBSchedule 是一款非常優秀的高性能分佈式調度框架,廣泛應用於阿里巴巴、淘寶、支付寶、京東、聚美、汽車之家、國美等很多互聯網企業的流程調度系統。tbschedule 在時間調度方面雖然沒有 quartz 強大,但是它支持分片功能。和 quartz 不同的是,tbschedule 使用 ZooKeeper 來實現任務調度的高可用和分片。

TBSchedule 的分佈式機制是通過靈活的 Sharding 方式實現的,分片的規則由客戶端決定,比如可以按所有數據的 ID 按 10 取模分片、按月份分片等等。TBSchedule 的宿主服務器可以進行動態擴容和資源回收,這個特點主要是因爲它後端依賴的 ZooKeeper,這裏的 ZooKeeper 對於 TBSchedule 來說是一個 NoSQL,用於存儲策略、任務、心跳信息數據,它的數據結構類似文件系統的目錄結構,它的節點有臨時節點、持久節點之分。調度引擎啓動後,隨着業務量數據量的增多,當前 Cluster 可能不能滿足目前的處理需求,那麼就需要增加服務器數量,一個新的服務器上線後會在 ZooKeeper 中創建一個代表當前服務器的一個唯一性路徑(臨時節點),並且新上線的服務器會和 ZooKeeper 保持長連接,當通信斷開後,節點會自動摘除。

TBSchedule 會定時掃描當前服務器的數量,重新進行任務分配。TBSchedule 不僅提供了服務端的高性能調度服務,還提供了一個 scheduleConsole 的 war 包,隨着宿主應用的部署直接部署到服務器,可以通過 web 的方式對調度的任務、策略進行監控管理,以及實時更新調整。

Saturn

Saturn 是唯品會在 github 開源的一款分佈式任務調度產品。它是基於噹噹 elastic-job 1.0 版本來開發的,其上完善了一些功能和添加了一些新的 feature。

亮點:

支持多語言開發 python、Go、Shell、Java、Php。

管理控制檯和數據統計分析更加完善

缺點:

技術文檔較少 , 該框架是 2016 年由唯品會的研發團隊基於 elastic-job 開發而來

Opencron

比較小衆,不推薦

Antares

Antares 是一款基於 Quartz 機制的分佈式任務調度管理平臺,內部重寫執行邏輯,一個任務僅會被服務器集羣中的某個節點調度。用戶可通過對任務預分片,有效提升任務執行效率;也可通過控制檯 antares-tower 對任務進行基本操作,如觸發,暫停,監控等。

Antares 是基於 Quartz 的分佈式調度,支持分片,支持樹形任務依賴,但是不是跨平臺的

sia-task

沒有仔細研究,依賴 Zookeeper,感覺更偏向於解決編排和跨平臺

無論是互聯網應用或者企業級應用,都充斥着大量的批處理任務。我們常常需要一些任務調度系統幫助我們解決問題。隨着微服務化架構的逐步演進,單體架構逐漸演變爲分佈式、微服務架構。在此的背景下,很多原先的任務調度平臺已經不能滿足業務系統的需求。於是出現了一些基於分佈式的任務調度平臺。這些平臺各有其特點,但各有不足之處,比如不支持任務編排、與業務高耦合、不支持跨平臺等問題。不是非常符合公司的需求,因此我們開發了微服務任務調度平臺(SIA-TASK)。

SIA 是我們公司基礎開發平臺 Simple is Awesome 的簡稱,SIA-TASK(微服務任務調度平臺)是其中的一項重要產品,SIA-TASK 契合當前微服務架構模式,具有跨平臺,可編排,高可用,無侵入,一致性,異步並行,動態擴展,實時監控等特點。

對比和結論

XXL-Job 側重的業務實現的簡單和管理的方便,學習成本簡單,失敗策略和路由策略豐富。推薦使用在 “用戶基數相對少,服務器數量在一定範圍內” 的情景下使用
Elastic-Job 關注的是數據,增加了彈性擴容和數據分片的思路,以便於更大限度的利用分佈式服務器的資源。但是學習成本相對高些,推薦在 “數據量龐大,且部署服務器數量較多” 時使用

對於併發場景不是特別高的系統來說,xxl-job 配置部署簡單易用,不需要引入多餘的組件,同時提供了可視化的控制檯,使用起來非常友好,是一個比較好的選擇。

Elastic-Job

這個框架大概在 2 年前很火,當時使用的公司很多,想必很多人也聽過了,但是很可惜現在已經不在維護了,代碼已經有 2 年沒有更新了,這裏違反了更新頻率的原則,如果出現問題可能都沒什麼人幫助你,所以並不是很推薦使用。

更傾向於選擇 XXL-JOB:

  1. 輕量級,支持通過 Web 頁面對任務進行動態 CRUD 操作,操作簡單

  2. 只依賴數據庫作爲集羣註冊中心,接入開發簡單,不需要 ZK

  3. 高可用、解耦、高性能、監控報警、分片、重試、故障轉移

  4. 團隊持續開發,社區活躍

  5. 支持後臺直接查看每個任務執行實時日誌,ELASTIC-JOB 中應該是沒有這個功能

ShedLock 集成更簡單,如果驗證通過,也未必不是一個好選擇!

其他

中心化調度 vs 去中心化調度

1xj4dd

中心化調度

去中心化調度

參考

XXL-JOB 和 Elastic-Job 對比

分佈式定時任務調度框架實踐

source:https://www.yuque.com/ssslinppp/blogs/bvlz5y

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/L-0WXIgcrhiinsW5t_Y3Ww