SpringBoot定時任務 - 開箱即用分散式任務框架xxl-job

語言: CN / TW / HK

除了前文介紹的ElasticJob,xxl-job在很多中小公司有著應用(雖然其程式碼和設計等質量並不太高,License不夠開放,有著個人主義色彩,但是其具體開箱使用的便捷性和功能相對完善性,這是中小團隊採用的主要原因);XXL-JOB是一個分散式任務排程平臺,其核心設計目標是開發迅速、學習簡單、輕量級、易擴充套件。本文介紹XXL-JOB以及SpringBoot的整合。@pdai

知識準備

需要對分散式任務的知識體系和xxl-Job有基本的理解。@pdai

什麼是xxl-job

XXL-JOB是一個分散式任務排程平臺,其核心設計目標是開發迅速、學習簡單、輕量級、易擴充套件。現已開放原始碼並接入多家公司線上產品線,開箱即用。如下內容來源於xxl-job官網

支援如下特性:

  • 1、簡單:支援通過Web頁面對任務進行CRUD操作,操作簡單,一分鐘上手;
  • 2、動態:支援動態修改任務狀態、啟動/停止任務,以及終止執行中任務,即時生效;
  • 3、排程中心HA(中心式):排程採用中心式設計,“排程中心”自研排程元件並支援叢集部署,可保證排程中心HA;
  • 4、執行器HA(分散式):任務分散式執行,任務"執行器"支援叢集部署,可保證任務執行HA;
  • 5、註冊中心: 執行器會週期性自動註冊任務, 排程中心將會自動發現註冊的任務並觸發執行。同時,也支援手動錄入執行器地址;
  • 6、彈性擴容縮容:一旦有新執行器機器上線或者下線,下次排程時將會重新分配任務;
  • 7、觸發策略:提供豐富的任務觸發策略,包括:Cron觸發、固定間隔觸發、固定延時觸發、API(事件)觸發、人工觸發、父子任務觸發;
  • 8、排程過期策略:排程中心錯過排程時間的補償處理策略,包括:忽略、立即補償觸發一次等;
  • 9、阻塞處理策略:排程過於密集執行器來不及處理時的處理策略,策略包括:單機序列(預設)、丟棄後續排程、覆蓋之前排程;
  • 10、任務超時控制:支援自定義任務超時時間,任務執行超時將會主動中斷任務;
  • 11、任務失敗重試:支援自定義任務失敗重試次數,當任務失敗時將會按照預設的失敗重試次數主動進行重試;其中分片任務支援分片粒度的失敗重試;
  • 12、任務失敗告警;預設提供郵件方式失敗告警,同時預留擴充套件介面,可方便的擴充套件簡訊、釘釘等告警方式;
  • 13、路由策略:執行器叢集部署時提供豐富的路由策略,包括:第一個、最後一個、輪詢、隨機、一致性HASH、最不經常使用、最近最久未使用、故障轉移、忙碌轉移等;
  • 14、分片廣播任務:執行器叢集部署時,任務路由策略選擇"分片廣播"情況下,一次任務排程將會廣播觸發叢集中所有執行器執行一次任務,可根據分片引數開發分片任務;
  • 15、動態分片:分片廣播任務以執行器為維度進行分片,支援動態擴容執行器叢集從而動態增加分片數量,協同進行業務處理;在進行大資料量業務操作時可顯著提升任務處理能力和速度。
  • 16、故障轉移:任務路由策略選擇"故障轉移"情況下,如果執行器叢集中某一臺機器故障,將會自動Failover切換到一臺正常的執行器傳送排程請求。
  • 17、任務進度監控:支援實時監控任務進度;
  • 18、Rolling實時日誌:支援線上檢視排程結果,並且支援以Rolling方式實時檢視執行器輸出的完整的執行日誌;
  • 19、GLUE:提供Web IDE,支援線上開發任務邏輯程式碼,動態釋出,實時編譯生效,省略部署上線的過程。支援30個版本的歷史版本回溯。
  • 20、指令碼任務:支援以GLUE模式開發和執行指令碼任務,包括Shell、Python、NodeJS、PHP、PowerShell等型別指令碼;
  • 21、命令列任務:原生提供通用命令列任務Handler(Bean任務,"CommandJobHandler");業務方只需要提供命令列即可;
  • 22、任務依賴:支援配置子任務依賴,當父任務執行結束且執行成功後將會主動觸發一次子任務的執行, 多個子任務用逗號分隔;
  • 23、一致性:“排程中心”通過DB鎖保證叢集分散式排程的一致性, 一次任務排程只會觸發一次執行;
  • 24、自定義任務引數:支援線上配置排程任務入參,即時生效;
  • 25、排程執行緒池:排程系統多執行緒觸發排程執行,確保排程精確執行,不被堵塞;
  • 26、資料加密:排程中心和執行器之間的通訊進行資料加密,提升排程資訊保安性;
  • 27、郵件報警:任務失敗時支援郵件報警,支援配置多郵件地址群發報警郵件;
  • 28、推送maven中央倉庫: 將會把最新穩定版推送到maven中央倉庫, 方便使用者接入和使用;
  • 29、執行報表:支援實時檢視執行資料,如任務數量、排程次數、執行器數量等;以及排程報表,如排程日期分佈圖,排程成功分佈圖等;
  • 30、全非同步:任務排程流程全非同步化設計實現,如非同步排程、非同步執行、非同步回撥等,有效對密集排程進行流量削峰,理論上支援任意時長任務的執行;
  • 31、跨語言:排程中心與執行器提供語言無關的 RESTful API 服務,第三方任意語言可據此對接排程中心或者實現執行器。除此之外,還提供了 “多工模式”和“httpJobHandler”等其他跨語言方案;
  • 32、國際化:排程中心支援國際化設定,提供中文、英文兩種可選語言,預設為中文;
  • 33、容器化:提供官方docker映象,並實時更新推送dockerhub,進一步實現產品開箱即用;
  • 34、執行緒池隔離:排程執行緒池進行隔離拆分,慢任務自動降級進入"Slow"執行緒池,避免耗盡排程執行緒,提高系統穩定性;
  • 35、使用者管理:支援線上管理系統使用者,存在管理員、普通使用者兩種角色;
  • 36、許可權控制:執行器維度進行許可權控制,管理員擁有全量許可權,普通使用者需要分配執行器許可權後才允許相關操作;

xxl-job的架構設計

設計思想

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

將任務抽象成分散的JobHandler,交由“執行器”統一管理,“執行器”負責接收排程請求並執行對應的JobHandler中業務邏輯。

因此,“排程”和“任務”兩部分可以相互解耦,提高系統整體穩定性和擴充套件性;

系統組成

  1. 排程模組(排程中心)
  2. 負責管理排程資訊,按照排程配置發出排程請求,自身不承擔業務程式碼。排程系統與任務解耦,提高了系統可用性和穩定性,同時排程系統性能不再受限於任務模組;
  3. 支援視覺化、簡單且動態的管理排程資訊,包括任務新建,更新,刪除,GLUE開發和任務報警等,所有上述操作都會實時生效,同時支援監控排程結果以及執行日誌,支援執行器Failover。
  4. 執行模組(執行器):
  5. 負責接收排程請求並執行任務邏輯。任務模組專注於任務的執行等操作,開發和維護更加簡單和高效;
  6. 接收“排程中心”的執行請求、終止請求和日誌請求等。

架構圖

實現案例

主要介紹SpringBoot整合xxl-job的方式:Bean模式(基於方法和基於類); 以及基於線上配置程式碼/指令碼的GLUE模式。

Bean模式(基於方法)

Bean模式任務,支援基於方法的開發方式,每個任務對應一個方法。基於方法開發的任務,底層會生成JobHandler代理,和基於類的方式一樣,任務也會以JobHandler的形式存在於執行器任務容器中。

優點

  • 每個任務只需要開發一個方法,並新增”@XxlJob”註解即可,更加方便、快速。
  • 支援自動掃描任務並注入到執行器容器。

缺點:要求Spring容器環境;

Job的開發環境依賴

Maven 依賴

xml <dependency> <groupId>com.xuxueli</groupId> <artifactId>xxl-job-core</artifactId> <version>2.3.1</version> </dependency>

application.properties配置

```yml

web port

server.port=8081

no web

spring.main.web-environment=false

log config

logging.config=classpath:logback.xml

xxl-job admin address list, such as "http://address" or "http://address01,http://address02"

xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin

xxl-job, access token

xxl.job.accessToken=default_token

xxl-job executor appname

xxl.job.executor.appname=xxl-job-executor-sample

xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null

xxl.job.executor.address=

xxl-job executor server-info

xxl.job.executor.ip= xxl.job.executor.port=9999

xxl-job executor log-path

xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler

xxl-job executor log-retention-days

xxl.job.executor.logretentiondays=30

```

Config配置(PS:這裡我是直接拿的xxl-job demo中的配置,實際開發中可以封裝一個starter自動注入)

```java package tech.pdai.springboot.xxljob.config;

import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;

/* * xxl-job config * * @author xuxueli 2017-04-28 / @Configuration public class XxlJobConfig { private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);

@Value("${xxl.job.admin.addresses}")
private String adminAddresses;

@Value("${xxl.job.accessToken}")
private String accessToken;

@Value("${xxl.job.executor.appname}")
private String appname;

@Value("${xxl.job.executor.address}")
private String address;

@Value("${xxl.job.executor.ip}")
private String ip;

@Value("${xxl.job.executor.port}")
private int port;

@Value("${xxl.job.executor.logpath}")
private String logPath;

@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;


@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
    logger.info(">>>>>>>>>>> xxl-job config init.");
    XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
    xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
    xxlJobSpringExecutor.setAppname(appname);
    xxlJobSpringExecutor.setAddress(address);
    xxlJobSpringExecutor.setIp(ip);
    xxlJobSpringExecutor.setPort(port);
    xxlJobSpringExecutor.setAccessToken(accessToken);
    xxlJobSpringExecutor.setLogPath(logPath);
    xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);

    // Bean方法模式
    // 通過掃描@XxlJob方式註冊

    // 註冊Bean類模式
    XxlJobExecutor.registJobHandler("beanClassDemoJobHandler", new BeanClassDemoJob());

    return xxlJobSpringExecutor;
}

}

```

Job的開發

開發步驟:

  1. 任務開發:在Spring Bean例項中,開發Job方法;
  2. 註解配置:為Job方法添加註解 "@XxlJob(value="自定義jobhandler名稱", init = "JobHandler初始化方法", destroy = "JobHandler銷燬方法")",註解value值對應的是排程中心新建任務的JobHandler屬性的值。
  3. 執行日誌:需要通過 "XxlJobHelper.log" 列印執行日誌;
  4. 任務結果:預設任務結果為 "成功" 狀態,不需要主動設定;如有訴求,比如設定任務結果為失敗,可以通過 "XxlJobHelper.handleFail/handleSuccess" 自主設定任務結果;

```java package tech.pdai.springboot.xxljob.job;

import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.Arrays;

import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component;

/* * XxlJob開發示例(Bean模式 - 方法) * / @Slf4j @Component public class BeanMethodDemoJob {

/**
 * 1、簡單任務示例(Bean模式)
 */
@XxlJob("demoJobHandler")
public void demoJobHandler() {
    XxlJobHelper.log("demoJobHandler execute...");
}

/**
 * 2、分片廣播任務
 */
@XxlJob("shardingJobHandler")
public void shardingJobHandler() throws Exception {
    // logback console日誌
    log.info("shardingJobHandler execute...");

    // 通過xxl記錄到DB中的日誌
    XxlJobHelper.log("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);
        }
    }

}


/**
 * 3、命令列任務
 */
@XxlJob("commandJobHandler")
public void commandJobHandler() throws Exception {
    XxlJobHelper.log("commandJobHandler execute...");

    String command = XxlJobHelper.getJobParam();
    int exitValue = -1;

    BufferedReader bufferedReader = null;
    try {
        // command process
        ProcessBuilder processBuilder = new ProcessBuilder();
        processBuilder.command(command);
        processBuilder.redirectErrorStream(true);

        Process process = processBuilder.start();
        //Process process = Runtime.getRuntime().exec(command);

        BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream());
        bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream));

        // command log
        String line;
        while ((line = bufferedReader.readLine())!=null) {
            XxlJobHelper.log(line);
        }

        // command exit
        process.waitFor();
        exitValue = process.exitValue();
    } catch (Exception e) {
        XxlJobHelper.log(e);
    } finally {
        if (bufferedReader!=null) {
            bufferedReader.close();
        }
    }

    if (exitValue==0) {
        // default success
    } else {
        XxlJobHelper.handleFail("command exit value(" + exitValue + ") is failed");
    }

}


/**
 * 4、跨平臺Http任務
 * 引數示例:
 * "url: http://www.baidu.com\n" +
 * "method: get\n" +
 * "data: content\n";
 */
@XxlJob("httpJobHandler")
public void httpJobHandler() throws Exception {
    XxlJobHelper.log("httpJobHandler execute...");

    // param parse
    String param = XxlJobHelper.getJobParam();
    if (param==null || param.trim().length()==0) {
        XxlJobHelper.log("param[" + param + "] invalid.");

        XxlJobHelper.handleFail();
        return;
    }

    String[] httpParams = param.split("\n");
    String url = null;
    String method = null;
    String data = null;
    for (String httpParam : httpParams) {
        if (httpParam.startsWith("url:")) {
            url = httpParam.substring(httpParam.indexOf("url:") + 4).trim();
        }
        if (httpParam.startsWith("method:")) {
            method = httpParam.substring(httpParam.indexOf("method:") + 7).trim().toUpperCase();
        }
        if (httpParam.startsWith("data:")) {
            data = httpParam.substring(httpParam.indexOf("data:") + 5).trim();
        }
    }

    // param valid
    if (url==null || url.trim().length()==0) {
        XxlJobHelper.log("url[" + url + "] invalid.");

        XxlJobHelper.handleFail();
        return;
    }
    if (method==null || !Arrays.asList("GET", "POST").contains(method)) {
        XxlJobHelper.log("method[" + method + "] invalid.");

        XxlJobHelper.handleFail();
        return;
    }
    boolean isPostMethod = method.equals("POST");

    // request
    HttpURLConnection connection = null;
    BufferedReader bufferedReader = null;
    try {
        // connection
        URL realUrl = new URL(url);
        connection = (HttpURLConnection) realUrl.openConnection();

        // connection setting
        connection.setRequestMethod(method);
        connection.setDoOutput(isPostMethod);
        connection.setDoInput(true);
        connection.setUseCaches(false);
        connection.setReadTimeout(5 * 1000);
        connection.setConnectTimeout(3 * 1000);
        connection.setRequestProperty("connection", "Keep-Alive");
        connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
        connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8");

        // do connection
        connection.connect();

        // data
        if (isPostMethod && data!=null && data.trim().length() > 0) {
            DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
            dataOutputStream.write(data.getBytes("UTF-8"));
            dataOutputStream.flush();
            dataOutputStream.close();
        }

        // valid StatusCode
        int statusCode = connection.getResponseCode();
        if (statusCode!=200) {
            throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid.");
        }

        // result
        bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
        StringBuilder result = new StringBuilder();
        String line;
        while ((line = bufferedReader.readLine())!=null) {
            result.append(line);
        }
        String responseMsg = result.toString();

        XxlJobHelper.log(responseMsg);

        return;
    } catch (Exception e) {
        XxlJobHelper.log(e);

        XxlJobHelper.handleFail();
        return;
    } finally {
        try {
            if (bufferedReader!=null) {
                bufferedReader.close();
            }
            if (connection!=null) {
                connection.disconnect();
            }
        } catch (Exception e2) {
            XxlJobHelper.log(e2);
        }
    }

}

/**
 * 5、生命週期任務示例:任務初始化與銷燬時,支援自定義相關邏輯;
 */
@XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy")
public void demoJobHandler2() throws Exception {
    XxlJobHelper.log("demoJobHandler2, execute...");
}

public void init() {
    log.info("init");
}

public void destroy() {
    log.info("destroy");
}

}

```

(@pdai: 從設計的角度,xxl-job可以對上述不同型別進行細分)

Job的排程配置和執行

新增Job, 並把上述的@XxlJob(value="自定義jobhandler名稱", init = "JobHandler初始化方法", destroy = "JobHandler銷燬方法")中 自定義jobhandler名稱 填寫到JobHandler中。

其它配置如下:

可以選擇操作中執行一次任務,或者啟動(按照Cron執行)

可以檢視執行的記錄

進一步可以看每個執行記錄的執行日誌

Bean模式(基於類)

Bean模式任務,支援基於類的開發方式,每個任務對應一個Java類。

優點:不限制專案環境,相容性好。即使是無框架專案,如main方法直接啟動的專案也可以提供支援,可以參考示例專案 “xxl-job-executor-sample-frameless”;

缺點

  • 每個任務需要佔用一個Java類,造成類的浪費;
  • 不支援自動掃描任務並注入到執行器容器,需要手動注入。

Job的開發環境依賴

同Bean模式(基於方法)

Job的開發

開發步驟:

  1. 執行器專案中,開發Job類:
    • 開發一個繼承自"com.xxl.job.core.handler.IJobHandler"的JobHandler類,實現其中任務方法。
    • 手動通過如下方式注入到執行器容器。
  2. 註冊jobHandler
    • XxlJobExecutor.registJobHandler("xxxxxJobHandler", new xxxxxJobHandler());

Job開發

```java package tech.pdai.springboot.xxljob.job;

import com.xxl.job.core.handler.IJobHandler; import lombok.extern.slf4j.Slf4j;

/* * @author pdai / @Slf4j public class BeanClassDemoJob extends IJobHandler {

@Override
public void execute() throws Exception {
    log.info("BeanClassDemoJob, execute...");
}

} ```

註冊jobHandler(@pdai: 這裡xxl-job設計的不好,是可以通過IJobHandler來自動註冊的)

java XxlJobExecutor.registJobHandler("beanClassDemoJobHandler", new BeanClassDemoJob());

啟動SpringBoot應用, 可以發現註冊的

java ... 20:34:15.385 logback [main] INFO c.x.job.core.executor.XxlJobExecutor - >>>>>>>>>>> xxl-job register jobhandler success, name:beanClassDemoJobHandler, jobHandler:tech.pdai.springboot.xxljob.job.BeanClassDemoJob@640ab13c ...

Job的排程配置和執行

同Bean模式(基於方法)

在排程器中新增執行後,後臺執行的日誌如下:

java 20:41:00.021 logback [xxl-job, EmbedServer bizThreadPool-1023773196] INFO c.x.job.core.executor.XxlJobExecutor - >>>>>>>>>>> xxl-job regist JobThread success, jobId:5, handler:tech.pdai.springboot.xxljob.job.BeanClassDemoJob@640ab13c 20:41:00.022 logback [xxl-job, JobThread-5-1654681260021] INFO t.p.s.xxljob.job.BeanClassDemoJob - BeanClassDemoJob, execute...

GLUE模式

任務以原始碼方式維護在排程中心,支援通過Web IDE線上更新,實時編譯和生效,因此不需要指定JobHandler。

配置和啟動流程

開發流程如下:

建立GLUE型別的Job(這裡以Java為例)

選中指定任務,點選該任務右側“GLUE”按鈕,將會前往GLUE任務的Web IDE介面,在該介面支援對任務程式碼進行開發(也可以在IDE中開發完成後,複製貼上到編輯中)。

版本回溯功能(支援30個版本的版本回溯):在GLUE任務的Web IDE介面,選擇右上角下拉框“版本回溯”,會列出該GLUE的更新歷史,選擇相應版本即可顯示該版本程式碼,儲存後GLUE程式碼即回退到對應的歷史版本;

執行後的記錄如下

GLUE模式還有哪些

xxl-job一共支援如下幾種GLUE模式:

  • GLUE模式(Java):任務以原始碼方式維護在排程中心;該模式的任務實際上是一段繼承自IJobHandler的Java類程式碼並 "groovy" 原始碼方式維護,它在執行器專案中執行,可使用@Resource/@Autowire注入執行器裡中的其他服務;
  • GLUE模式(Shell):任務以原始碼方式維護在排程中心;該模式的任務實際上是一段 "shell" 指令碼;
  • GLUE模式(Python):任務以原始碼方式維護在排程中心;該模式的任務實際上是一段 "python" 指令碼;
  • GLUE模式(PHP):任務以原始碼方式維護在排程中心;該模式的任務實際上是一段 "php" 指令碼;
  • GLUE模式(NodeJS):任務以原始碼方式維護在排程中心;該模式的任務實際上是一段 "nodejs" 指令碼;
  • GLUE模式(PowerShell):任務以原始碼方式維護在排程中心;該模式的任務實際上是一段 "PowerShell" 指令碼;

更多配置的說明

```bash + 基礎配置: - 執行器:任務的繫結的執行器,任務觸發排程時將會自動發現註冊成功的執行器, 實現任務自動發現功能; 另一方面也可以方便的進行任務分組。每個任務必須繫結一個執行器, 可在 "執行器管理" 進行設定; - 任務描述:任務的描述資訊,便於任務管理; - 負責人:任務的負責人; - 報警郵件:任務排程失敗時郵件通知的郵箱地址,支援配置多郵箱地址,配置多個郵箱地址時用逗號分隔;

  • 觸發配置:
  • 排程型別:
    • 無:該型別不會主動觸發排程;
    • CRON:該型別將會通過CRON,觸發任務排程;
    • 固定速度:該型別將會以固定速度,觸發任務排程;按照固定的間隔時間,週期性觸發;
    • 固定延遲:該型別將會以固定延遲,觸發任務排程;按照固定的延遲時間,從上次排程結束後開始計算延遲時間,到達延遲時間後觸發下次排程;
  • CRON:觸發任務執行的Cron表示式;
  • 固定速度:韌體速度的時間間隔,單位為秒;
  • 固定延遲:韌體延遲的時間間隔,單位為秒;

  • 高階配置:

    • 路由策略:當執行器叢集部署時,提供豐富的路由策略,包括; FIRST(第一個):固定選擇第一個機器; LAST(最後一個):固定選擇最後一個機器; ROUND(輪詢):; RANDOM(隨機):隨機選擇線上的機器; CONSISTENT_HASH(一致性HASH):每個任務按照Hash演算法固定選擇某一臺機器,且所有任務均勻雜湊在不同機器上。 LEAST_FREQUENTLY_USED(最不經常使用):使用頻率最低的機器優先被選舉; LEAST_RECENTLY_USED(最近最久未使用):最久未使用的機器優先被選舉; FAILOVER(故障轉移):按照順序依次進行心跳檢測,第一個心跳檢測成功的機器選定為目標執行器併發起排程; BUSYOVER(忙碌轉移):按照順序依次進行空閒檢測,第一個空閒檢測成功的機器選定為目標執行器併發起排程; SHARDING_BROADCAST(分片廣播):廣播觸發對應叢集中所有機器執行一次任務,同時系統自動傳遞分片引數;可根據分片引數開發分片任務;
    • 子任務:每個任務都擁有一個唯一的任務ID(任務ID可以從任務列表獲取),當本任務執行結束並且執行成功時,將會觸發子任務ID所對應的任務的一次主動排程。
    • 排程過期策略:
      • 忽略:排程過期後,忽略過期的任務,從當前時間開始重新計算下次觸發時間;
      • 立即執行一次:排程過期後,立即執行一次,並從當前時間開始重新計算下次觸發時間;
    • 阻塞處理策略:排程過於密集執行器來不及處理時的處理策略; 單機序列(預設):排程請求進入單機執行器後,排程請求進入FIFO佇列並以序列方式執行; 丟棄後續排程:排程請求進入單機執行器後,發現執行器存在執行的排程任務,本次請求將會被丟棄並標記為失敗; 覆蓋之前排程:排程請求進入單機執行器後,發現執行器存在執行的排程任務,將會終止執行中的排程任務並清空佇列,然後執行本地排程任務;
    • 任務超時時間:支援自定義任務超時時間,任務執行超時將會主動中斷任務;
    • 失敗重試次數;支援自定義任務失敗重試次數,當任務失敗時將會按照預設的失敗重試次數主動進行重試; ```

示例原始碼

http://github.com/realpdai/tech-pdai-spring-demos

更多內容

告別碎片化學習,無套路一站式體系化學習後端開發: Java 全棧知識體系 http://pdai.tech