宜信開源|分佈式任務調度平臺SIA-TASK的架構設計與運行流程

1、分佈式任務調度的背景

不管是互聯網應用或者企業級應用,都充斥着大量的批處理任務。咱們經常須要一些任務調度系統來幫助解決問題。隨着微服務化架構的逐步演進,單體架構逐漸演變爲分佈式、微服務架構。在此背景下,不少原先的任務調度平臺已經不能知足業務系統的需求,因而出現了一些基於分佈式的任務調度平臺。java

1.1 分佈式任務調度的演進

在實際業務開發過程當中,不少時候咱們無可避免地須要使用一些定時任務來解決問題。一般咱們會有多種解決方案:使用 Crontab 或 SpringCron (固然這種狀況可能機器不多並且任務簡單又不是不少的狀況下)。然而,當應用複雜度升高、定時任務數量增多且任務之間產生依賴關係時,Crontab 進行定時任務的管理配置就會很是混亂,嚴重影響工做效率。這時就會產生一系列問題:git

  • 任務管理混亂,生命週期沒法統一協調管理;
  • 任務之間若是存在依賴關係,難以編排。

隨着互聯網的發展,分佈式服務架構勢愈來愈流行。相應的也須要一個分佈式任務調度系統來管理分佈式架構中的定時任務。github

1.2 分佈式任務調度架構

分佈式任務調度設計

當垂直應用愈來愈多,應用之間交互也會愈來愈複雜,一般咱們採用分佈式或者微服務架構,將核心業務抽取出來,造成單獨的服務。一個獨立的微服務羣體逐漸造成穩定的服務中心,使得業務應用能更快地響應多變的市場需求。web

此時,用於提升業務複用及整合的分佈式服務框架成爲關鍵。同時,因爲服務獨立,通常能作到定時任務獨立的狀況,任務的更改對於總體系統的影響小之又小。一般咱們會採用任務與調度分離的方式(如上圖所示),任務的執行邏輯無需關注調度與編排,同時能夠保證執行器和調度的高可用,易於開發和維護。算法

1.3 分佈式任務調度優點

在分佈式服務架構的基礎上,因爲獨立業務的數量可能不少,此時若是定時任務單獨在該服務中實現,極可能會出現難以管理的狀況,且避免不了因爲定時任務的更改而致使的業務重啓。所以,一個獨立的分佈式任務調度系統是很必要的,能夠用來全局統籌管理全部的定時任務。同時,將任務的配置單獨抽離出來,做爲該分佈式任務調度系統的功能,就能作到定時任務的更改不影響任何業務,也不影響整個系統:數據庫

  • 經過調度與任務分離的方式進行管理,大大下降了開發和維護成本;
  • 分佈式部署,保證了系統的高可用性、伸縮性、負載均衡,提升了容錯性;
  • 能夠經過控制檯部署和管理定時任務,方便靈活高效;
  • 任務均可以持久化到數據庫,避免了宕機和數據丟失帶來的隱患,同時有完善的任務失敗重作機制和詳細的任務跟蹤及告警策略。

2、分佈式任務調度技術選型

2.1 分佈式任務調度考慮因素

sia-task-設計圖

  • 任務編排:多個業務之間的定時任務存在流程次序。
  • 任務分片:對於一個大型任務,須要分片並行執行。
  • 跨平臺:除了使用 Java 技術棧(SpringBoot、Spring等)的項目以外,還有使用其餘語言的應用。
  • 無侵入:業務不但願與調度高耦合,只關注業務的執行邏輯。
  • 故障轉移:任務執行過程當中遇到問題有補償措施,減小人工介入。
  • 高可用:調度系統自身必須保證高可用。
  • 實時監控:實時獲取任務的執行狀態。
  • 可視化:任務調度的操做提供可視化頁面,方便使用。
  • 動態編輯:業務的任務時鐘參數可能變更,不但願停機部署。

2.2 SIA-TASK與其它分佈式任務調度技術比較

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

開源地址:https://github.com/siaorg/sia-taskjson

咱們先對比市場上主流的開源分佈式任務調度框架,分析其優缺點,而後再介紹咱們的技術選型。後端

  • Quartz: Quartz 是 OpenSymphony 開源組織在任務調度領域的一個開源項目,徹底基於 Java 實現。該項目於 2009 年被 Terracotta 收購,目前是 Terracotta 旗下的一個項目。相比於 JDK 或 Spring 提供的定時任務,Quartz 對單個任務的控制基本作到了極致,以其強大功能和應用靈活性,在企業應用中發揮了巨大的做用。然而 Quartz 並不支持任務的編排(任務之間有依賴),並且不支持任務分片。
  • TBSchedule: TBSchedule 是一個支持分佈式的調度框架,能讓一種批量任務或者不斷變化的任務,被動態地分配到多個主機的 JVM 中,不一樣的線程組中並行執行。基於 ZooKeeper 的純 Java 實現,由 Alibaba 開源。TBSchedule 側重於任務的分發,支持任務分片,可是沒有任務編排,也不是跨平臺的。
  • Elastic-Job: Elastic-Job 是噹噹開源的一個分佈式調度解決方案,由兩個相互獨立的子項目Elastic-Job-Lite 和 Elastic-Job-Cloud 組成。Elastic-Job 支持任務分片(做業分片一致性),可是沒有任務編排,也不是跨平臺的。
  • Saturn: Saturn 是惟品會開源的分佈式,高可用的調度服務。Saturn 在 Elastic-Job 作二次開發,支持監控、任務分片、跨平臺,可是沒有任務編排。
  • Antares: Antares 是基於 Quartz 的分佈式調度,支持分片、支持樹形任務依賴,但不是跨平臺的。
  • Uncode-Schedule: Uncode-Schedule 是基於 Zookeeper 的分佈式任務調度組件。支持全部任務在集羣中不重複、不遺漏的執行。支持動態添加和刪除任務。可是不支持任務分片,也沒有任務編排,還不是跨平臺的。
  • XXL-JOB: XXL-JOB 是一個輕量級分佈式任務調度平臺,其核心設計目標是開發迅速、學習簡單、輕量級、易擴展。XXL-JOB 支持分片,簡單支持任務依賴,支持子任務依賴,不是跨平臺的。

下面咱們簡單對比下 SIA-TASK 與這些任務調度框架:服務器

任務編排 任務分片 跨平臺 高可用 故障轉移 實時監控
SIA-TASK
Quartz × × .NET × API監控
TBSchedule × ×
Elastic-Job × ×
Saturn ×
Antares ×
Uncode-Schedule × × ×
XXL-JOB 子任務依賴 ×

能夠發現,這些調度框架基本上都支持高可用、故障轉移與實時監控等功能,可是對於任務編排、任務分片與跨平臺等功能的支持各有側重點。SIA-TASK 將全面支持這些功能。

3、SIA-TASK介紹

3.1 SIA-TASK技術選型

sia-task-technology

  • REST:一種軟件架構風格。要求執行器暴露 Http 調用接口來達到跨平臺的目的。
  • AOP:切面編程技術。在 Spring 項目擴展包 Hunter 中使用,保證 Task 被串行調用(單例單線程)。
  • Quartz:功能強大,應用靈活,對單個任務的控制基本作到了極致,用來做爲調度中心時鐘組件。
  • MySQL:用於元數據存儲與(暫時)日誌存取。
  • Elastic:基於 Lucene 的搜索服務器,提供了一個分佈式多用戶能力的全文搜索引擎,用於日誌的存儲與查詢。
  • SpringCloud:社區活躍的開發框架,也是公司指定的統一開發框架。用於快速開發,快速迭代。
  • MyBatis:一款優秀的持久層框架,支持定製化 SQL,存儲過程以及高級映射。用於簡化持久層開發。
  • Zookeeper:久經考驗的註冊中心。用來解決調度中心高可用,分佈式一致性等問題。

3.2 SIA-TASK設計思想

SIA-TASK借鑑微服務設計思想,獲取分佈在每一個執行器節點上的任務(Task)元數據,進行彙報,上傳註冊中心。利用在線可編輯方式支持任務在線編排、動態修改任務時鐘;使用 Http 協議做爲交互傳輸協議。數據交互格式統一使用Json。用戶經過編排器(下文會作介紹)進行操做,觸發事件,調度器接收事件,由調度中心進行時鐘解析,執行任務流程,進行任務通知。

3.3 SIA-TASK基本概念

SIA-TASK 採用任務和調度分離的方式,業務的執行任務邏輯和調度邏輯徹底分離。系統組成共涉及如下幾個核心概念:

  • 任務(Task): 基本執行單元,執行器對外暴露的一個HTTP調用接口。
  • 做業(Job): 由一個或者多個存在相互邏輯關係(串行/並行)的任務組成,任務調度中心調度的最小單位。
  • 計劃(Plan): 由若干個順序執行的做業組成,每一個做業都有本身的執行週期,計劃沒有執行週期。
  • 任務調度中心(Scheduler): 根據每一個的做業的執行週期進行調度,即按照計劃、做業、任務的邏輯進行HTTP請求。
  • 任務編排中心(Config): 編排中心使用任務來建立計劃和做業。
  • 任務執行器(Executer): 接收HTTP請求進行業務邏輯的執行。
  • Hunter:Spring項目擴展包,負責執行器中的任務抓取,上傳註冊中心,業務可依賴該組件進行Task編寫。

3.4 SIA-TASK系統架構

SIA-TASK 能夠分爲三大模塊(調度中心、編排中心和執行器)、兩大組件(持久化存儲和註冊中心)。這三大模塊和兩大組件的做用以下:

  • 任務調度中心:負責搶佔Job和任務調度以及任務遷移等,是SIA-TASK 的核心功能模塊。
  • 任務編排中心:負責對在線任務進行邏輯編排,提供日誌查看和實時監控功能。
  • 任務執行器:負責接收調度請求並執行任務邏輯。
  • 任務註冊中心(ZK):協調Job和Task、調度器等的工做流程。
  • 持久化存儲(DB):記錄項目的Job和Task數據,並提供日誌存儲。

SIA-TASK 使用 SpringBoot 體系做爲架構選型,基於Quartz及Zookeeper進行二次開發,支持相應的特性功能,SIA-TASK 的邏輯架構圖以下圖所示:

邏輯架構圖

3.5 SIA-TASK模塊說明

3.5.1 任務調度中心

任務調度中心負責任務調度,管理調度信息,按照調度配置發出調度請求,自身不承擔業務代碼。調度系統與任務解耦,提升了系統可用性和穩定性,同時調度系統性能再也不受限於任務模塊;支持可視化、簡單且動態地管理調度信息,包括任務新建,更新,刪除和任務報警等,全部上述操做都會實時生效,同時支持監控調度結果以及執行日誌,支持執行器故障恢復。

3.5.2 任務編排中心

任務編排中心是分佈式調度中心支持在線任務模型編排的組件;依託於UI可進行web端任務編排。

咱們能夠經過上述基礎模型來編排一些複雜的調度模型,例如:

調度模型

SIA-TASK的UI編排界面:

UI編排界面

編排結束後查看task的編排信息以下圖所示:

編排信息

同時,編排中心還提供首頁統計數據查看、調度監控、Job管理、Task管理以及日誌管理功能。

3.5.3 任務執行器

負責接收調度請求並執行任務邏輯。任務模塊專一於任務的執行等操做,開發和維護更加簡單和高效;

執行器支持兩種類型:

(1) 若是使用 sia-task-hunter,支持SpringBoot項目和Spring項目, 引入 sia-task-hunter,任務(Task)抓取客戶端。合規的HTTP接口(稱之爲Task)任務會自動被抓取並上傳註冊中心;

(2) 若是不使用 sia-task-hunter,只需提供任務可調用的HTTP接口,此時須要業務手動錄入,且自行控制該任務的併發調用控制。

3.5.4 任務註冊中心(Zookeeper)

分佈式框架採用Zookeeper做爲註冊中心。

註冊中心

(1) 任務註冊

調度中心和執行集羣都以Zookeeper做爲註冊中心,全部數據以節點及節點內容的形式註冊,經過定時彙報主機狀態保持存活在Zookeeper上。

(2) 元數據存儲

註冊中心不只僅提供註冊服務,而且存儲每一個執行器的信息(包括執行器實例信息,執行器上傳的Task元數據,以及任務運行時的一些臨時狀態數據)。

(3) 事件發佈

基於Zookeeper事件推送機制,進行任務的發佈,經過平衡算法保證調度器任務搶佔的分佈均衡。

(4) 負載均衡

保證調度器獲取執行Job的個數均衡,避免單一節點壓力。

3.5.5 持久化存儲(DB)

這裏採用MySQL做爲數據持久化解決方案。

除了Task動態元數據保存在註冊中心以外,其餘相關的元數據都存入MySQL,包括但不限於:手動錄入的Task、配置的Job信息、編排的Task依賴信息、調度日誌、業務人員操做日誌、Task執行日誌等。

3.6 SIA-TASK關鍵運行流程

3.6.1 任務發佈流程

任務發佈流程

(1) 用戶能夠經過UI進行Job建立。能夠選擇Job類型,設置預警郵箱,設置Job描述。而後爲建立的Job進行任務Task編排。

(2) Job建立完畢而且設置Task編排關係後可進行任務發佈,經過UI對相應的Job進行操做(激活,執行一次,中止以及刪除操做)。

(3) 用戶的Task任務能夠是經過抓取器抓取的,亦可使用UI手動建立。

3.6.2 執行流程

執行流程

(1) Job建立完成以後,能夠選擇激活觸發定時任務;

(2) Job到達預訂時間後,調度中心觸發Job,而後按照預約的Task編排邏輯經過http通知Task執行器進行執行,並異步監放任務執行結果;

(3) 若執行結果成功,則判斷是否存在後置Task,若存在,則繼續下一次調度,若不存在,則說明該Job執行完畢,結束本次調用;若執行結果失敗,則觸發故障恢復策略:當即中止、忽略本次失敗、屢次嘗試、轉到其它執行器執行。

3.6.3 狀態流轉

Job在整個生命週期內存在四種狀態,分別是:已中止(NULL)、準備中(READY)、開始運行(RUNNING)、異常中止(STOP),狀態流轉及流轉條件以下圖所示。

狀態流轉

3.7 SIA-TASK模塊設計

SIA-TASK 的物理網絡拓撲圖以下所示:

網絡拓撲圖

SIA-TASK 的模塊間交互設計思路:

(1) 經過編排中心建立Task任務或經過Hunter自動抓取,並將 Task 信息異步保存到DB;建立Job並激活,在zookeeper中建立JobKey。

(2) 調度中心會監聽zookeeper中JobKey建立事件,而後搶佔建立的Job,搶佔成功後加入quartz定時任務,當時間到達即觸發Job運行。調度中心異步調用執行器服務執行Job中的 Task (可能存在多個 Task ,遵循 Task 失敗策略),並將結果返回到調度中心。

(3) 將Job執行狀態隨時在zookeeper上更改,經過編排中心的查詢接口能夠進行查詢。

(4) Job執行結束後,等待下一次執行。

3.7.1 任務編排中心設計

編排中心能夠與DB和zookeeper進行數據交互,其主要功能可分爲三方面:

  • 數據持久化接口服務;
  • zookeeper上元數據變動;
  • 數據可視化:查看系統各類統計數據等。

編排中心首頁監控展現以下:

首頁監控

3.7.2 任務調度中心設計

調度中心主要與DB、ZK和執行器進行交互,其主要功能可分爲如下幾個方面:

  • Job執行日誌記錄
  • ZK中Job狀態變動
  • 調用執行器服務執行Job
  • 調度中心高可用
  • Job 調度線程池

3.7.3 任務執行器設計

執行器能夠與ZK和調度中心進行交互,其主要功能可分爲兩個方面:

  • 接受調度中心的調度,執行定時任務,並將結果返回到調度中心;
  • 自動抓取執行器上的 Task 任務,提交到ZK。

執行器 Task示例:

@OnlineTask(description = "在線任務示例",enableSerial=true)
@RequestMapping(value = "/example", method = { RequestMethod.POST }, produces = "application/json;charset=UTF-8")
@CrossOrigin(methods = { RequestMethod.POST }, origins = "*")
@ResponseBody
public String example(@RequestBody String json) {   
    /**
     * TODO:客戶端業務邏輯處理
     */
    Map<String, String> info = new HashMap<String, String>();
    info.put("status", "success");
    info.put("result", "as you need");
    return JSONHelper.toString(info);
}

因而可知,任務 Task 編寫很是簡單。

3.8 SIA-TASK高可用設計

分佈式服務通常都要考慮高可用方案,一樣 SIA-TASK 爲了保證高可用,針對不一樣的服務組件進行了不一樣維度加強。

3.8.1 任務編排中心的高可用

SIA-TASK 經過先後端分離、服務拆分等措施實現了編排中心的高可用。當集羣中某實例失效後,不會影響集羣的其它實例,所以無需特殊操做便可使用集羣中其它的可用編排中心。

3.8.2 任務調度中心的高可用

3.8.2.1 異常轉移

若是調度中心集羣中的某個實例節點服務宕機後,這個實例節點上的全部Job會平滑遷移到集羣中可用的實例上,不會形成定時任務的執行缺失,同時,當崩潰後的實例修復成功從新接入該集羣時,會繼續搶佔Job提供服務。

3.8.2.2 配置線程池

調度採用線程池方式實現,避免單線程因阻塞而引發任務調度延遲。程池裏的線程數,默認值是10,當執行任務會併發執行多個耗時任務時,要根據業務特色選擇線程池的大小。

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 60
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

SIA-TASK 根據quartz自身提供的threadPool再次進行線程池的利用。進行線程池從新定義,針對每一個Job去分配一個獨有的線程池。線程池的大小可根據Job自身編排的 Task 個數的大小進行動態伸縮,從而保證每一個Job的調度線程徹底獨立,不在會由於編排 Task 個數的陡增而耗盡線程資源。同時提供線程池資源的回收邏輯,在Job進行永久性終止時回收爲期分配的線程池資源。

public static ExecutorService getExecutorService(String JobKey) {

    ExecutorService exec = executorPool.get(JobKey);
     if (exec == null) {
        LOGGER.info(Constants.LOG_PREFIX + "Initialize thread pool for running Jobs,Job is {}",JobKey);
      exec = Executors.newCachedThreadPool();
      executorPool.putIfAbsent(JobKey, exec);
      exec = executorPool.get(JobKey);
  }
    return exec;
}
3.8.2.3 全日誌跟蹤

SIA-TASK 針對Job的整個調度生命週期進行全面跟蹤,利用AOP進行日誌加強,調度中心每觸發一次Job調度就會進行日誌記錄。同時針對Job編排的 Task 執行也會進行記錄任務日誌。

日誌分爲Job日誌和 Task 日誌:

  • Job日誌:包含調度器信息、調度時間、調度狀態以及其餘附加屬性。
  • Task日誌:包含執行器信息、執行時間、執行狀態、返回信息以及其餘附加屬性。
3.8.2.4 異步封裝
  • SIA-TASK 從一開始設計就考慮了任務進行遠程調用對調度中心併發線程資源的損耗。對於Job封裝的 Task 遠程調度,所有采用異步調用,每次任務請求邏輯的耗時很是的輕量化。只僅僅一次見到的http請求。
  • 支持 Task 進行用戶自定義超時設置,支持兩種模式的超時:connecttimeout、readtimeout。支持用戶根據業務的具體執行週期來進行超時設置。
public interface RestTemplate {

/**
 * 異步Post方法 * @param request
 * @param responseType
 * @param uriVariables
 * @param <T>
 * @return
 */
 <T> ListenableFuture<ResponseEntity<T>> postAsyncForEntity(Request request, Class<T> responseType, Object... uriVariables); }
3.8.2.5 自定義調度器資源池

調度器資源池

SIA-TASK 從物理資源角度設計了調度資源池,出於一些特殊狀況的考量咱們針對調度器進行了池化;調度器能夠經過不一樣的操做進行狀態的轉變,從而進行能力的轉化。

  • 工做調度器資源池:管理具有獲取任務能力而且能夠實際獲取任務的調度器資源。
  • 下線調度器資源池:管理具有獲取任務能力可是實際不容許獲取的調度器資源。
  • 離線調度器資源池:管理下線調度器資源池中已經宕機的調度器資源。

3.8.3 任務執行器的高可用

  • 考慮網絡的不穩定性,SIA-TASK 針對網絡的不穩定性也作出了很是重要的設計,對於節點的連通性的測試支持以及針對 Task 運行實例節點健康的預感知,保證提早感知 Task 實例節點的健康狀況,保證調度 Task 高可用。

  • 同時也保證了執行器實例針對網絡致使連接中斷的問題,SIA-TASK 從新設計了zookeeper的重連機制,保證 Task 運行實例節點因網絡問題丟失連接後還能進行恢復重試,直到恢復正常後併入執行池中正常接收任務的調度。

  • 通常來講,執行器也是集羣部署的。做爲 Task 的執行單元,若是在執行器集羣中一臺機器上執行失敗,調度中心會根據失敗策略來作故障轉移。這裏提供了兩種故障轉移策略:輪詢轉移和最大補償轉移。輪詢轉移爲對可用的執行器列表進行輪詢,如有一個執行器執行成功,則 Task 執行成功,若所有執行失敗,則 Task 執行失敗。最大補償轉移爲首先在本執行器再次執行若干次,若執行成功,則不會轉移,若仍是執行失敗,則執行輪詢轉移策略。

4、總結

至此對微服務任務調度平臺 SIA-TASK 作了一個簡要的介紹,包括設計背景、架構設計以及產品組件功能與特性。微服務任務調度平臺 SIA-TASK 基本上解決了當前的業務需求,提供簡單高效的編排調度服務。SIA-TASK 會持續迭代,提供更爲完善的服務。以後也會提供相關技術文檔和使用文檔。

連接指南

開源地址:https://github.com/siaorg/sia-task

做者:毛正衛/李鵬飛/梁鑫

相關文章
相關標籤/搜索