不管是互聯網應用或者企業級應用,都充斥着大量的批處理任務。咱們經常須要一些任務調度系統來幫助解決問題。隨着微服務化架構的逐步演進,單體架構逐漸演變爲分佈式、微服務架構。在此背景下,不少原先的任務調度平臺已經不能知足業務系統的需求,因而出現了一些基於分佈式的任務調度平臺。java
在實際業務開發過程當中,不少時候咱們無可避免地須要使用一些定時任務來解決問題。一般咱們會有多種解決方案:使用 Crontab 或 SpringCron (固然這種狀況可能機器不多並且任務簡單又不是不少的狀況下)。然而,當應用複雜度升高、定時任務數量增多且任務之間產生依賴關係時,Crontab 進行定時任務的管理配置就會很是混亂,嚴重影響工做效率。這時就會產生一系列問題:git
隨着互聯網的發展,分佈式服務架構勢愈來愈流行。相應的也須要一個分佈式任務調度系統來管理分佈式架構中的定時任務。github
當垂直應用愈來愈多,應用之間交互也會愈來愈複雜,一般咱們採用分佈式或者微服務架構,將核心業務抽取出來,造成單獨的服務。一個獨立的微服務羣體逐漸造成穩定的服務中心,使得業務應用能更快地響應多變的市場需求。web
此時,用於提升業務複用及整合的分佈式服務框架成爲關鍵。同時,因爲服務獨立,通常能作到定時任務獨立的狀況,任務的更改對於總體系統的影響小之又小。一般咱們會採用任務與調度分離的方式(如上圖所示),任務的執行邏輯無需關注調度與編排,同時能夠保證執行器和調度的高可用,易於開發和維護。算法
在分佈式服務架構的基礎上,因爲獨立業務的數量可能不少,此時若是定時任務單獨在該服務中實現,極可能會出現難以管理的狀況,且避免不了因爲定時任務的更改而致使的業務重啓。所以,一個獨立的分佈式任務調度系統是很必要的,能夠用來全局統籌管理全部的定時任務。同時,將任務的配置單獨抽離出來,做爲該分佈式任務調度系統的功能,就能作到定時任務的更改不影響任何業務,也不影響整個系統:數據庫
SIA是宜信公司基礎開發平臺Simple is Awesome的簡稱,SIA-TASK(微服務任務調度平臺)是其中的一項重要產品,SIA-TASK契合當前微服務架構模式,具備跨平臺、可編排、高可用、無侵入、一致性、異步並行、動態擴展、實時監控等特色。編程
開源地址:https://github.com/siaorg/sia-taskjson
咱們先對比市場上主流的開源分佈式任務調度框架,分析其優缺點,而後再介紹咱們的技術選型。後端
下面咱們簡單對比下 SIA-TASK 與這些任務調度框架:服務器
任務編排 | 任務分片 | 跨平臺 | 高可用 | 故障轉移 | 實時監控 | |
---|---|---|---|---|---|---|
SIA-TASK | √ | √ | √ | √ | √ | √ |
Quartz | × | × | .NET | √ | × | API監控 |
TBSchedule | × | √ | × | √ | √ | √ |
Elastic-Job | × | √ | × | √ | √ | √ |
Saturn | × | √ | √ | √ | √ | √ |
Antares | √ | √ | × | √ | √ | √ |
Uncode-Schedule | × | × | × | √ | √ | √ |
XXL-JOB | 子任務依賴 | √ | × | √ | √ | √ |
能夠發現,這些調度框架基本上都支持高可用、故障轉移與實時監控等功能,可是對於任務編排、任務分片與跨平臺等功能的支持各有側重點。SIA-TASK 將全面支持這些功能。
SIA-TASK借鑑微服務設計思想,獲取分佈在每一個執行器節點上的任務(Task)元數據,進行彙報,上傳註冊中心。利用在線可編輯方式支持任務在線編排、動態修改任務時鐘;使用 Http 協議做爲交互傳輸協議。數據交互格式統一使用Json。用戶經過編排器(下文會作介紹)進行操做,觸發事件,調度器接收事件,由調度中心進行時鐘解析,執行任務流程,進行任務通知。
SIA-TASK 採用任務和調度分離的方式,業務的執行任務邏輯和調度邏輯徹底分離。系統組成共涉及如下幾個核心概念:
SIA-TASK 能夠分爲三大模塊(調度中心、編排中心和執行器)、兩大組件(持久化存儲和註冊中心)。這三大模塊和兩大組件的做用以下:
SIA-TASK 使用 SpringBoot 體系做爲架構選型,基於Quartz及Zookeeper進行二次開發,支持相應的特性功能,SIA-TASK 的邏輯架構圖以下圖所示:
任務調度中心負責任務調度,管理調度信息,按照調度配置發出調度請求,自身不承擔業務代碼。調度系統與任務解耦,提升了系統可用性和穩定性,同時調度系統性能再也不受限於任務模塊;支持可視化、簡單且動態地管理調度信息,包括任務新建,更新,刪除和任務報警等,全部上述操做都會實時生效,同時支持監控調度結果以及執行日誌,支持執行器故障恢復。
任務編排中心是分佈式調度中心支持在線任務模型編排的組件;依託於UI可進行web端任務編排。
咱們能夠經過上述基礎模型來編排一些複雜的調度模型,例如:
SIA-TASK的UI編排界面:
編排結束後查看task的編排信息以下圖所示:
同時,編排中心還提供首頁統計數據查看、調度監控、Job管理、Task管理以及日誌管理功能。
負責接收調度請求並執行任務邏輯。任務模塊專一於任務的執行等操做,開發和維護更加簡單和高效;
執行器支持兩種類型:
(1) 若是使用 sia-task-hunter,支持SpringBoot項目和Spring項目, 引入 sia-task-hunter,任務(Task)抓取客戶端。合規的HTTP接口(稱之爲Task)任務會自動被抓取並上傳註冊中心;
(2) 若是不使用 sia-task-hunter,只需提供任務可調用的HTTP接口,此時須要業務手動錄入,且自行控制該任務的併發調用控制。
分佈式框架採用Zookeeper做爲註冊中心。
(1) 任務註冊
調度中心和執行集羣都以Zookeeper做爲註冊中心,全部數據以節點及節點內容的形式註冊,經過定時彙報主機狀態保持存活在Zookeeper上。
(2) 元數據存儲
註冊中心不只僅提供註冊服務,而且存儲每一個執行器的信息(包括執行器實例信息,執行器上傳的Task元數據,以及任務運行時的一些臨時狀態數據)。
(3) 事件發佈
基於Zookeeper事件推送機制,進行任務的發佈,經過平衡算法保證調度器任務搶佔的分佈均衡。
(4) 負載均衡
保證調度器獲取執行Job的個數均衡,避免單一節點壓力。
這裏採用MySQL做爲數據持久化解決方案。
除了Task動態元數據保存在註冊中心以外,其餘相關的元數據都存入MySQL,包括但不限於:手動錄入的Task、配置的Job信息、編排的Task依賴信息、調度日誌、業務人員操做日誌、Task執行日誌等。
(1) 用戶能夠經過UI進行Job建立。能夠選擇Job類型,設置預警郵箱,設置Job描述。而後爲建立的Job進行任務Task編排。
(2) Job建立完畢而且設置Task編排關係後可進行任務發佈,經過UI對相應的Job進行操做(激活,執行一次,中止以及刪除操做)。
(3) 用戶的Task任務能夠是經過抓取器抓取的,亦可使用UI手動建立。
(1) Job建立完成以後,能夠選擇激活觸發定時任務;
(2) Job到達預訂時間後,調度中心觸發Job,而後按照預約的Task編排邏輯經過http通知Task執行器進行執行,並異步監放任務執行結果;
(3) 若執行結果成功,則判斷是否存在後置Task,若存在,則繼續下一次調度,若不存在,則說明該Job執行完畢,結束本次調用;若執行結果失敗,則觸發故障恢復策略:當即中止、忽略本次失敗、屢次嘗試、轉到其它執行器執行。
Job在整個生命週期內存在四種狀態,分別是:已中止(NULL)、準備中(READY)、開始運行(RUNNING)、異常中止(STOP),狀態流轉及流轉條件以下圖所示。
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執行結束後,等待下一次執行。
編排中心能夠與DB和zookeeper進行數據交互,其主要功能可分爲三方面:
編排中心首頁監控展現以下:
調度中心主要與DB、ZK和執行器進行交互,其主要功能可分爲如下幾個方面:
執行器能夠與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 編寫很是簡單。
分佈式服務通常都要考慮高可用方案,一樣 SIA-TASK 爲了保證高可用,針對不一樣的服務組件進行了不一樣維度加強。
SIA-TASK 經過先後端分離、服務拆分等措施實現了編排中心的高可用。當集羣中某實例失效後,不會影響集羣的其它實例,所以無需特殊操做便可使用集羣中其它的可用編排中心。
若是調度中心集羣中的某個實例節點服務宕機後,這個實例節點上的全部Job會平滑遷移到集羣中可用的實例上,不會形成定時任務的執行缺失,同時,當崩潰後的實例修復成功從新接入該集羣時,會繼續搶佔Job提供服務。
調度採用線程池方式實現,避免單線程因阻塞而引發任務調度延遲。程池裏的線程數,默認值是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; }
SIA-TASK 針對Job的整個調度生命週期進行全面跟蹤,利用AOP進行日誌加強,調度中心每觸發一次Job調度就會進行日誌記錄。同時針對Job編排的 Task 執行也會進行記錄任務日誌。
日誌分爲Job日誌和 Task 日誌:
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); }
SIA-TASK 從物理資源角度設計了調度資源池,出於一些特殊狀況的考量咱們針對調度器進行了池化;調度器能夠經過不一樣的操做進行狀態的轉變,從而進行能力的轉化。
考慮網絡的不穩定性,SIA-TASK 針對網絡的不穩定性也作出了很是重要的設計,對於節點的連通性的測試支持以及針對 Task 運行實例節點健康的預感知,保證提早感知 Task 實例節點的健康狀況,保證調度 Task 高可用。
同時也保證了執行器實例針對網絡致使連接中斷的問題,SIA-TASK 從新設計了zookeeper的重連機制,保證 Task 運行實例節點因網絡問題丟失連接後還能進行恢復重試,直到恢復正常後併入執行池中正常接收任務的調度。
通常來講,執行器也是集羣部署的。做爲 Task 的執行單元,若是在執行器集羣中一臺機器上執行失敗,調度中心會根據失敗策略來作故障轉移。這裏提供了兩種故障轉移策略:輪詢轉移和最大補償轉移。輪詢轉移爲對可用的執行器列表進行輪詢,如有一個執行器執行成功,則 Task 執行成功,若所有執行失敗,則 Task 執行失敗。最大補償轉移爲首先在本執行器再次執行若干次,若執行成功,則不會轉移,若仍是執行失敗,則執行輪詢轉移策略。
至此對微服務任務調度平臺 SIA-TASK 作了一個簡要的介紹,包括設計背景、架構設計以及產品組件功能與特性。微服務任務調度平臺 SIA-TASK 基本上解決了當前的業務需求,提供簡單高效的編排調度服務。SIA-TASK 會持續迭代,提供更爲完善的服務。以後也會提供相關技術文檔和使用文檔。
連接指南
開源地址:https://github.com/siaorg/sia-task
做者:毛正衛/李鵬飛/梁鑫