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

除了前文介紹的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. 調度模塊(調度中心)
    1. 負責管理調度信息,按照調度配置發出調度請求,自身不承擔業務代碼。調度系統與任務解耦,提高了系統可用性和穩定性,同時調度系統性能不再受限於任務模塊;
    2. 支持可視化、簡單且動態的管理調度信息,包括任務新建,更新,刪除,GLUE開發和任務報警等,所有上述操作都會實時生效,同時支持監控調度結果以及執行日誌,支持執行器Failover。
  2. 執行模塊(執行器):
    1. 負責接收調度請求並執行任務邏輯。任務模塊專注於任務的執行等操作,開發和維護更加簡單和高效;
    2. 接收“調度中心”的執行請求、終止請求和日誌請求等。

架構圖

實現案例

主要介紹SpringBoot集成xxl-job的方式:Bean模式(基於方法和基於類); 以及基於在線配置代碼/腳本的GLUE模式。

Bean模式(基於方法)

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

優點

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

缺點:要求Spring容器環境;

Job的開發環境依賴

Maven 依賴

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

application.properties配置

# 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自動注入)

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" 自主設置任務結果;
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開發

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來自動註冊的)

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

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

...
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模式(基於方法)

在調度器中添加執行後,後臺執行的日誌如下:

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" 腳本;

更多配置的說明

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

+ 觸發配置:
  - 調度類型:
      + 無:該類型不會主動觸發調度;
      + 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隊列並以串行方式運行;
        丟棄後續調度:調度請求進入單機執行器後,發現執行器存在運行的調度任務,本次請求將會被丟棄並標記爲失敗;
        覆蓋之前調度:調度請求進入單機執行器後,發現執行器存在運行的調度任務,將會終止運行中的調度任務並清空隊列,然後運行本地調度任務;
    - 任務超時時間:支持自定義任務超時時間,任務運行超時將會主動中斷任務;
    - 失敗重試次數;支持自定義任務失敗重試次數,當任務失敗時將會按照預設的失敗重試次數主動進行重試;

示例源碼

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

更多內容

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章