分布式系統:分布式任務調度xxl-job較深入使用



       xxl-job是一個分布式定時任務調度框架,功能強大,底層使用自己實現的rpc框架進行注冊和管理,數據庫使用mysql,調度觸發使用數據庫鎖來作為調度鎖。

       xxl-job主要分為調度中心admin以及任務,任務引入依賴jar包並配置啟動類為spring所管理的bean后,將自動通過spring-bean提供的initMethod進行啟動線程選擇一個端口進行注冊以及監聽任務調度。

       公司目前引入xxl-job框架代替quartz框架作為分布式任務調度組件,並在其之上進行一定開發以及優化,所以這篇文章主要分享一些深入使用,主要是概念的詳細介紹。

系統關鍵概念介紹

執行器

       配置中心配置的執行器,概念上對應執行定時任務的服務,支持分布式調度以及調度的各種路由規則配置。注冊方式支持自動注冊和手動配置機器地址兩種方式,心跳時間間隔默認為30s,失效時間90s。

       執行器自動注冊后,調度中心頁面依舊有最長30秒的延遲顯示,原因是數據庫中注冊表更新后,展示執行器的表是由另一個守護線程去更新的,更新頻率為默認心跳時間30s,所以管理台展示會有延遲,但不影響任務調度。

任務

       任務以執行器為維度配置,每個任務必須屬於一個執行器,當任務觸發時會根據該任務所屬的執行器去尋找執行器的地址列表,然后通過配置的路由規則以及阻塞規則去去執行。

       任務支持本地任務以及遠程任務,本地任務即按照執行方寫好的業務邏輯執行。遠程任務通過GLUE,在調度中心管理台寫好代碼,分發到執行方去執行。建議無特殊需求的話,統一使用本地任務。

任務配置項描述

任務配置項描述

1. 執行器:選擇該任務由哪個執行器去執行
2. 任務描述:簡單描述該任務的功能以及作用,如:訂單定時跑批
3. 路由策略:設置任務執行時,如何去選擇執行器,高頻任務建議使用一致性哈希或者第一台執行
4. Cron:Cron表達式,描述任務運行的時間
5. 運行模式:BEAN即為接入服務配置在本地對應的handler運行,其他方式均為管理台設置代碼交由接入服務遠程執行
6. JobHandler:運行模式為BEAN時必填,值應當為接入服務本地執行任務的handler
7. 阻塞策略:當同一任務多次調度到同一台執行器時,執行器應當使用的策略
8. 子任務ID:如配置,則該任務完成后自動觸發一次子任務的執行
9. 任務超時時間:配置后當任務超時時將自動終止任務執行。
10. 失敗重試次數:任務失敗后重試的次數。
11. 負責人:一般為該任務接入方的負責人
12. 報警郵件:任務報警后發送的郵件地址
13. 任務參數:若配置了任務參數,任務調度時將發送任務參數至執行方handler。

阻塞策略

阻塞策略即同一個任務在執行器的阻塞執行策略。由執行器端控制。典型場景為:任務A分發到執行器A執行,此時任務A再次觸發並分發到執行器A,此時根據阻塞策略選擇的不同將會有以下三種執行策略:
1. 單機串行 該策略下,同一執行器收到同一任務的調度觸發時,若已有任務正在執行,會將后續的任務放入執行線程的隊列中,等待線程輪詢繼續執行,可能會導致線程隊列阻塞過多任務導致內存過高,高頻且耗時較長任務慎用
2. 丟棄后續調度 該策略下,同一執行器收到同一任務的調度觸發時,若已有任務正在執行,會直接丟棄后續同一任務的調度,推薦使用
3. 覆蓋之前調度 該策略下,同一執行器收到同一任務的調度觸發時,若已有任務正在執行,將會直接停止正在執行的任務(通過線程InterruptedException異常以及volatile變量判斷),並將新任務放入隊列。一般情況下不建議使用

路由策略

路由策略即任務在配置中心進行調度分發時,選擇執行器的策略。由配置中心端控制。典型場景為:任務A觸發執行,任務A對應的執行器有執行器A,B,C,D,此時根據路由策略的選擇將會有以下幾種分發情況
1. 第一個:始終選擇第一台執行器作為任務執行器,不論該任務執行器是否正常。
2. 最后一個:始終選擇最后一台作為任務執行器
3. 輪詢:每個執行器輪流執行
4. 隨機:隨機選擇一個執行器執行
5. 一致性HASH:根據任務ID做一致性哈希選擇執行器,同一個任務必定只分發到同一個執行器。高頻或耗時較長任務推薦使用
6. 最不經常使用:選擇平均使用頻率最低的執行器。
7. 最近最久未使用:選擇最近的最久未使用的執行器。
8. 故障轉移:分別進行心跳檢測,選擇第一台心跳檢測正常的機器執行。
9. 忙碌轉移:分別進行忙碌檢測,選擇第一台空閑的機器執行。
10. 分片廣播:廣播到所有執行器執行,並提供分片參數,分片參數獲取方式如下,應用在被觸發時動態獲取自己是第幾個分片,共有幾個分片:

日志問題

xxl-job相關日志使用默認使用slf4j作為日志框架,使用專門的API寫入日志時,會輸出2種日志,客戶端日志與服務端日志

客戶端日志

客戶端日志根據配置文件中配置的logpath指定,根據源碼分析,客戶端日志將通過FileOutputStream寫到對應文件,且無法通過配置修改,所以只好修改了源碼中的邏輯,改為該值為空未配置時,直接通過slf4j寫入。

服務端日志

使用xxljob的日志api輸出日志時,日志也會在調度管理台看到,能看到的原理是xxl-job管理台會通過rpc調用執行器的接口,執行器收到請求后從指定的日志文件中讀取執行的日志並返回,這里存在一個比較麻煩的問題,就是xxl-job這種日志的邏輯,無法很好的兼容到項目統一的日志模塊里,十分不便。

所以實際使用過程中,我們在xxl-job管理台查詢日志時,對其進行了改造,修改為不從rpc查詢,而是走我們日志管理的搜索引擎根據執行的jobid查詢相關日志,結合客戶端日志輸出的改造,從而統一xxl-job和我們系統間的日志管理。

框架目前發現的缺點以及存在的問題

  1. 目前定時任務的調度串行是依賴db鎖的,某個子微服務或者子系統內部使用還好,但是不適合整個公司級別共用,這點和數據庫的耦合比較高,並沒有本地緩存之類的,對數據庫的HA依賴非常高。
  2. 管理模塊以及權限模塊,組內和小公司用用可以,不適合作為多個系統多個部門之間共用的中間件。
  3. 管理台存在一些簡單的安全bug,sql注入和js腳本注入非常簡單。(公司的安全測試測出來的)
  4. 相關協議支持較差,使用自己實現的RPC協議,如果需要dubbo或者spring cloud,需要自己拓展。並且雖然底層是netty,但是本身對netty異常的封裝並不是很好,導致一些奇怪的網絡問題或者其他的協議,會報莫名其妙的錯,沒有一定netty理解的人是看不懂的,這點我提了Issue,不過出於這套框架解耦與獨立的設計,估計是不會支持dubbo和spring cloud的。
  5. 上面提到的,日志模塊更建議自己整體修改下寫入邏輯,管理台看不到無所謂,(畢竟定時任務也沒誰會去管理台看日志,並且這部分日志寫多了對性能和網絡開銷有影響的),寫到本地使用elk之類的再查就可以了。
  6. 定時任務觸發的流水號或者跟蹤id,需要改動原框架,否則也會影響后續日志追蹤的問題。

總體而言這是一個很不錯的框架,關於定時任務的執行器和調度器關系也很優雅,值得拓展或者進行一定定制開發。目前使用來,穩定性也沒有問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM