天天看點

分布式定時排程:xxl-job 最佳實踐詳解

一、定時任務概述

1.1. 什麼是定時任務

定時任務是按照指定時間周期運作任務。使用場景為在某個固定時間點執行,或者周期性的去執行某個任務,比如:每天晚上24點做資料彙總,定時發送短信等。

1.2. 常見定時任務方案

While + Sleep : 通過循環加休眠的方式定時執行

Timer和TimerTask實作 :JDK自帶的定時任務,可以實作簡單的間隔執行任務(在指定時間點執行某一任務,也能定時的周期性執行),無法實作按月曆去排程執行任務。

ScheduledExecutorService : Java并發包下,JDK1.5出現,是比較理想的定時任務實作方案。Eureka就使用的是它

QuartZ : 使用Quartz,它是一個異步任務排程架構,功能豐富,可以實作按月曆排程,支援持久化。

使用Spring Task,Spring 3.0後提供Spring Task實作任務排程,支援按月曆排程,相比Quartz功能稍簡單,但是在開發基本夠用,支援注解程式設計方式。

SpringBoot中的Schedule : 通過@[email protected]最實作定時任務,底層使用的是Spring Task

1.3. 分布式定時任務面臨的問題

遇到什麼問題

上述的定時任務都是集中式(單體項目使用)的定時任務,在分布式中将會面臨一些問題或不足

業務量大,單機性能瓶頸需要擴充

多台機器部署如何保證定時任務不重複執行

定時任務時間需要可調整,可以暫停

機器發生故障down機,定時任務依然可用,如何實作故障轉移

定時任務,執行日志是否可監控

1.4. 分布式定時任務xxl-job

XXL-JOB是一個分布式任務排程平台,于2015問世,其核心設計目标是開發迅速、學習簡單、輕量級、易擴充。現已開放源代碼并接入多家公司線上産品線,開箱即用。其具備且不止如下能力

簡單:支援通過Web頁面對任務進行CRUD操作,操作簡單,一分鐘上手;

動态:支援動态修改任務狀态、啟動/停止任務,以及終止運作中任務,即時生效;

排程中心HA(中心式):排程采用中心式設計,“排程中心”基于叢集Quartz實作并支援叢集部署,可保證排程中心HA;執行器HA(分布式):任務分布式執行,任務"執行器"支援叢集部署,可保證任務執行HA;

彈性擴容縮容:一旦有新執行器機器上線或者下線,下次排程時将會重新配置設定任務;

路由政策:執行器叢集部署時提供豐富的路由政策,包括:第一個、最後一個、輪詢、随機、一緻- 性HASH、最不經常使用、最近最久未使用、故障轉移、忙碌轉移等;

故障轉移:任務路由政策選擇"故障轉移"情況下,如果執行器叢集中某一台機器故障,将會自動Failover切換到一台正常的執行器發送排程請求。

任務失敗告警:預設提供郵件方式失敗告警,同時預留擴充接口,可方面的擴充短信、釘釘等告警方式;

具體見:https://github.com/xuxueli/xxl-job/tree/v2.0.0

二、xxl-job架構設計

2.1. 設計思想

将排程行為抽象形成“排程中心”公共平台,而平台自身并不承擔業務邏輯,“排程中心”負責發起排程請求。

将任務抽象成分散的JobHandler,交由“執行器”統一管理,“執行器”負責接收排程請求并執行對應的JobHandler中業務邏輯。是以,“排程”和“任務”兩部分可以互相解耦,提高系統整體穩定性和擴充性;

2.2. 架構設計圖

xxl-job分為 排程中心和執行器兩大子產品

排程子產品(排程中心)

負責管理排程資訊,按照排程配置發出排程請求,自身不承擔業務代碼。排程系統與任務解耦,提高了系統可用性和穩定性,同時排程系統性能不再受限于任務子產品;

支援可視化、簡單且動态的管理排程資訊,包括任務建立,更新,删除,GLUE開發和任務報警等,所有上述操作都會實時生效,同時支援監控排程結果以及執行日志,支援執行器Failover(故障轉移)。

執行子產品(執行器)

負責接收排程請求并執行任務邏輯。任務子產品專注于任務的執行等操作,開發和維護更加簡單和高效;

接收“排程中心”的執行請求、終止請求和日志請求等。

分布式定時排程:xxl-job 最佳實踐詳解

排程中心高可用

基于資料庫的叢集方案,資料庫選用Mysql;叢集分布式并發環境中進行定時任務排程時,會在各個節點會上報任務,存到資料庫中,執行時會從資料庫中取出觸發器來執行,如果觸發器的名稱和執行時間相同,則隻有一個節點去執行此任務。

并行排程

排程采用線程池方式實作,避免單線程因阻塞而引起任務排程延遲。XXL-JOB排程子產品預設采用并行機制,在多線程排程的情況下,排程子產品被阻塞的幾率很低,大大提高了排程系統的承載量。

XXL-JOB的不同任務之間并行排程、并行執行。XXL-JOB的單個任務,針對多個執行器是并行運作的,針對單個執行器是串行執行的。同時支援任務終止。

執行器(任務)高可用

執行器如若叢集部署,排程中心将會感覺到線上的所有執行器,如“127.0.0.1:9997, 127.0.0.1:9998, 127.0.0.1:9999”。多個執行器可以選擇“路由政策”來采用輪詢,随機等方式進行多機器排程。

當任務”路由政策”選擇”故障轉移(FAILOVER)”時,當排程中心每次發起排程請求時,會按照順序對執行器發出心跳檢測請求,第一個檢測為存活狀态的執行器将會被標明并發送排程請求。排程成功後,可在日志監控界面檢視“排程備注”

三、xxl-job 案例參考

在com.xxl.job.executor.service.jobhandler.SampleXxlJob中提供了簡單的定時任務執行個體

為友善使用者參考與快速實用,示例執行器内原生提供多個Bean模式任務Handler,可以直接配置實用,如下:

  • demoJobHandler:簡單示例任務,任務内部模拟耗時任務邏輯,使用者可線上體驗Rolling Log等功能;
  • shardingJobHandler:分片示例任務,任務内部模拟處理分片參數,可參考熟悉分片任務;
  • httpJobHandler:通用HTTP任務Handler;業務方隻需要提供HTTP連結等資訊即可,不限制語言、平台。示例任務入參如下:
/**
     * 1、簡單任務示例(Bean模式)
     */
    @XxlJob("demoJobHandler")
    public void demoJobHandler() throws Exception {
        XxlJobHelper.log(" demoJobHandler start");

        XxlJobHelper.log("XXL-JOB, Hello World.");
        logger.info("demoJobHandler execute......");
        // 注意xxl-job統一隻接受一個String類型的參數,如果有多個參數,請自定義規則,擷取到參數後自行切割
        String param = XxlJobHelper.getJobParam();
        for (int i = 0; i < 5; i++) {
            XxlJobHelper.log("beat at:" + i);
            TimeUnit.SECONDS.sleep(2);
        }
        XxlJobHelper.handleSuccess(" demoJobHandler complete");
    }


    /**
     * 2、分片廣播任務
     */
    @XxlJob("shardingJobHandler")
    public void shardingJobHandler() throws Exception {
        XxlJobHelper.log(" shardingJobHandler start");

        logger.info("shardingJobHandler execute......");

        // 分片參數
        int shardIndex = XxlJobHelper.getShardIndex();
        int shardTotal = XxlJobHelper.getShardTotal();

        XxlJobHelper.log("分片參數:目前分片序号 = {}, 總分片數 = {}", shardIndex, shardTotal);

        // 業務邏輯
        for (int i = 0; i < shardTotal; i++) {
            if (i == shardIndex) {
                XxlJobHelper.log("第 {} 片, 命中分片開始處理", i);
            } else {
                XxlJobHelper.log("第 {} 片, 忽略", i);
            }
        }
        XxlJobHelper.handleSuccess(" shardingJobHandler complete");
    }
           
【重要】 如果我們要寫自己的定時任務,參照上面方法,在方法上注解一個@XxlJob(“任務名字”) ,方法可以接受一個字元串參數,方法需要傳回ReturnT格式。