面試官:使用 xxl-job 怎樣解決任務重疊問題?
大家好,我是君哥。今天分享批量任務的重疊問題。
面試官:聊聊你用過的任務調度框架?
我:目前任務調度框架的選擇有很多,比如業內熟悉的 QuartZ,Spring Batch,xxl-job,以及新一代的任務框架 PowerJob 等。我本人使用最多的還是 xxl-job。
面試官:使用 xxl-job 遇到過任務重疊的問題嗎?
我:任務重疊是批量任務調度中經常遇到的問題,主流的分布式調度框架是可以解決部分任務重復問題的,但并不能解決全部問題。
面試官:能說一下 xxl-job 解決了哪些任務重疊的問題,還有哪些問題沒有解決嗎?
我:(下午來自 xxl-job 官網)
- FIRST(第一個):固定選擇第一個機器。
- LAST(最后一個):固定選擇最后一個機器。
- ROUND(輪詢):按照注冊機器的列表順序進行任務調度。
- RANDOM(隨機):隨機選擇在線的機器。
- CONSISTENT_HASH(一致性HASH):每個任務按照Hash算法固定選擇某一臺機器,且所有任務均勻散列在不同機器上。
- LEAST_FREQUENTLY_USED(最不經常使用):使用頻率最低的機器優先被選舉。
- LEAST_RECENTLY_USED(最近最久未使用):最久未使用的機器優先被選舉。
- FAILOVER(故障轉移):按照順序依次進行心跳檢測,第一個心跳檢測成功的機器選定為目標執行器并發起調度。
- BUSYOVER(忙碌轉移):按照順序依次進行空閑檢測,第一個空閑檢測成功的機器選定為目標執行器并發起調度。
- SHARDING_BROADCAST(分片廣播):廣播觸發對應集群中所有機器執行一次任務,同時系統自動傳遞分片參數;可根據分片參數開發分片任務。
雖然策略比較多,但是常用的策略就是固定在一臺機器上面跑或者輪詢策略。假設有一個密集的定時調度任務,每隔兩分鐘跑一次,如果在同一個機器上跑,遇到調用下游接口響應慢,或者處理的業務數據暴增,可能出現前一次任務還沒有完成,下一次任務已經發起了。為了防止任務重疊在一臺機器上,可以采用 xxl-job 輪詢的策略。
面試官:按照你剛才舉的例子,定時任務每隔兩分鐘跑一次,如果選擇固定在一臺機器上跑,比如選擇路由策略是 FIRST 或者 LAST,有多個任務重疊在一臺機器上,xxl-job 是怎樣解決的?
我:對于跑批間隔時間比較短的定時任務,因為調度很密集,執行器很容易造成任務阻塞。xxl-job 提供了 3 種阻塞處理策略:
- 單機串行(默認):調度請求進入單機執行器后,調度請求進入 FIFO 隊列并以串行方式運行。
- 丟棄后續調度:調度請求進入單機執行器后,發現執行器存在運行的調度任務,本次請求將會被丟棄并標記為失敗。
- 覆蓋之前調度:調度請求進入單機執行器后,發現執行器存在運行的調度任務,將會終止運行中的調度任務并清空隊列,然后運行本地調度任務。
采用單機串行的策略,如果前面的任務沒有處理完成,后面的任務只能排隊,等待被調度。
面試官:單機任務排隊執行,這種策略看上去沒有什么問題。你有遇到過相關問題嗎?
我:一般場景下是不會有影響的,但如果一個任務跟日期相關,有任務排隊到第二天才能執行,很可能會造成業務影響。
面試官:能舉一個具體的場景嗎?
我:舉一個貸款業務的例子,系統通過跑批任務給應還款日是第二天的客戶發送還款短信通知,處理邏輯是每個任務查出應還款日是第二天的一批用戶,然后發送短信通知。這里的第二天需要用系統日期來判斷。如果有排隊的任務到第二天才執行,那應還款的部分用戶會收不到通知。
面試官:這種場景 xxl-job 有解決方案嗎?
我:采用輪詢的路由策略,可以讓排隊的任務調度的不同的機器上,這樣可以減少單機器的任務積壓問題。
面試官:采用輪詢的路由策略,表面看是可以解決單機器的任務積壓問題。但如果任務積壓是因為下游接口響應慢、sql 查詢性能差等造成的任務執行慢,采用輪詢策略可以解決嗎?
我:這類問題造成的任務排隊,改為輪詢策略也是解決不了的,輪詢策略只能解決類似單機資源緊張造成跑批慢的情況。
面試官:那這些問題有什么解決方案嗎?
我:業務上是有解決方案的,比如金融行業系統里面一般有“切日”的概念,跟日期相關的業務不會取系統日期,而是取數據庫保存的賬務日期,所有需要使用賬務日期的任務跑批完成后,才會把數據庫保存的賬務日期切換到第二天。
面試官:采用輪詢路由策略,還可能帶來哪些問題呢?
我:還可能造成冥等的問題。比如一個任務的處理邏輯是,每隔兩分鐘從數據庫查詢 100 條狀態是“未處理”的數據進行處理,處理完成后更新狀態為“已處理”。如果機器一上的任務還沒有執行完成,機器二上的任務已經開始調度,那很可能會把機器一上正在執行的 100 條數據查出來重復處理。
面試官:這個問題該怎么解決呢?
我:我提供兩種解決思路:
- 第一,最簡單的方式就是采用單機執行;
- 第二,增加一個中間狀態“處理中”,執行查詢數據的時候采用排它鎖,查出后更新成中間狀態,提交事務后再執行處理邏輯,處理邏輯執行完成后更新成“已處理”
select * from xxx where statsu='未處理' limit yy,100 for update
update xxx set statsu='處理中' where id in(...)
面試官:那如果數據源不能加鎖呢?比如數據源是郵件、接口查詢。
我:可以對郵件或接口查到的數據唯一鍵進行保存,把唯一鍵做數據庫主鍵,下一個任務可以根據主鍵做排除,再實現業務邏輯。
面試官:好的,恭喜你進入下一輪...