天天看點

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。
  1. 執行子產品(執行器):
  1. 負責接收排程請求并執行任務邏輯。任務子產品專注于任務的執行等操作,開發和維護更加簡單和高效;
  2. 接收“排程中心”的執行請求、終止請求和日志請求等。

架構圖

SpringBoot定時任務 - 開箱即用分布式任務架構xxl-job

實作案例

主要介紹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中。

其它配置如下:

SpringBoot定時任務 - 開箱即用分布式任務架構xxl-job

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

SpringBoot定時任務 - 開箱即用分布式任務架構xxl-job

可以檢視執行的記錄

SpringBoot定時任務 - 開箱即用分布式任務架構xxl-job

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

SpringBoot定時任務 - 開箱即用分布式任務架構xxl-job

Bean模式(基于類)

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

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

缺點:

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

Job的開發環境依賴

同Bean模式(基于方法)

Job的開發

開發步驟:

  1. 執行器項目中,開發Job類:
  • 開發一個繼承自"com.xxl.job.core.handler.IJobHandler"的JobHandler類,實作其中任務方法。
  • 手動通過如下方式注入到執行器容器。
  1. 注冊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為例)

SpringBoot定時任務 - 開箱即用分布式任務架構xxl-job

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

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

SpringBoot定時任務 - 開箱即用分布式任務架構xxl-job

執行後的記錄如下

SpringBoot定時任務 - 開箱即用分布式任務架構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隊列并以串行方式運作;
        丢棄後續排程:排程請求進入單機執行器後,發現執行器存在運作的排程任務,本次請求将會被丢棄并标記為失敗;
        覆寫之前排程:排程請求進入單機執行器後,發現執行器存在運作的排程任務,将會終止運作中的排程任務并清空隊列,然後運作本地排程任務;
    - 任務逾時時間:支援自定義任務逾時時間,任務運作逾時将會主動中斷任務;
    - 失敗重試次數;支援自定義任務失敗重試次數,當任務失敗時将會按照預設的失敗重試次數主動進行重試;      

示例源碼

更多内容