除了前文介紹的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中業務邏輯。
因此,“調度”和“任務”兩部分可以相互解耦,提高系統整體穩定性和擴展性;
系統組成
- 調度模塊(調度中心)
- 負責管理調度信息,按照調度配置發出調度請求,自身不承擔業務代碼。調度系統與任務解耦,提高了系統可用性和穩定性,同時調度系統性能不再受限於任務模塊;
- 支持可視化、簡單且動態的管理調度信息,包括任務新建,更新,刪除,GLUE開發和任務報警等,所有上述操作都會實時生效,同時支持監控調度結果以及執行日誌,支持執行器Failover。
- 執行模塊(執行器):
- 負責接收調度請求並執行任務邏輯。任務模塊專注於任務的執行等操作,開發和維護更加簡單和高效;
- 接收“調度中心”的執行請求、終止請求和日誌請求等。
架構圖
實現案例
主要介紹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的開發
開發步驟:
- 任務開發:在Spring Bean實例中,開發Job方法;
- 註解配置:爲Job方法添加註解 "@XxlJob(value="自定義jobhandler名稱", init = "JobHandler初始化方法", destroy = "JobHandler銷燬方法")",註解value值對應的是調度中心新建任務的JobHandler屬性的值。
- 執行日誌:需要通過 "XxlJobHelper.log" 打印執行日誌;
- 任務結果:默認任務結果爲 "成功" 狀態,不需要主動設置;如有訴求,比如設置任務結果爲失敗,可以通過 "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的開發
開發步驟:
- 執行器項目中,開發Job類:
- 開發一個繼承自"com.xxl.job.core.handler.IJobHandler"的JobHandler類,實現其中任務方法。
- 手動通過如下方式注入到執行器容器。
- 註冊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