XXLJob是一個分散式任務排程平臺,優點:開發迅速、學習簡單、輕量級、易擴充套件。是大眾點評員工xxl建立並維護,基於 GPL-3.0 開源,可放心商用,目前已經擁有龐大的使用群體。
簡單來說,就是一個定時任務中介軟體,類似的產品有當當網開源的Elastic-Job。
由上可知,XXLJob為C/S架構,排程中心本身可以高可用部署,執行器整合在業務微服務中,當業務微服務多範例部署的時候,執行器也就可以達到分散式和高可用了。
排程中心依賴資料庫,安裝前需先初始化資料庫,初始化指令碼可從github中獲取 https://github.com/xuxueli/xxl-job/tree/master/doc/db
表說明:
參見官方檔案,本文重點放在SpringBoot整合XXLJob。排程中心的部署,尤其是高可用部署,後續單獨開篇。
排程中心就是一個SpringBoot程式,以xxljob2.4.0版本為例,其依賴的SpringBoot版本為 2.7.9,所以任何啟動SpringBoot 的方式都可以,webui的預設存取地址:http://ip:8080/xxl-job-admin
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.0</version>
</dependency>
server:
port: 9009
logging:
level:
com.ramble: debug
xxl:
job:
admin:
#排程中心部署根地址 [選填]:如排程中心叢集部署存在多個地址則用逗號分隔。執行器將會使用該地址進行"執行器心跳註冊"和"任務結果回撥";為空則關閉自動註冊;
addresses: http://127.0.0.1:8080/xxl-job-admin
#執行器通訊TOKEN [選填]:非空時啟用;
accessToken:
executor:
#執行器AppName [選填]:執行器心跳註冊分組依據;為空則關閉自動註冊
appname: xxljob-demo-service
#${spring.application.name}
#執行器註冊 [選填]:優先使用該設定作為註冊地址,為空時使用內嵌服務 」IP:PORT「 作為註冊地址。從而更靈活的支援容器型別執行器動態IP和動態對映埠問題。
address: ""
#執行器IP [選填]:預設為空表示自動獲取IP,多網路卡時可手動設定指定IP,該IP不會繫結Host僅作為通訊實用;地址資訊用於 "執行器註冊" 和 "排程中心請求並觸發任務";
ip: ""
#執行器埠號 [選填]:小於等於0則自動獲取;預設埠為9999,單機部署多個執行器時,注意要設定不同執行器埠;
port: 0
###${server-port}
#執行器執行紀錄檔檔案儲存磁碟路徑 [選填] :需要對該路徑擁有讀寫許可權;為空則使用預設路徑;
logpath: ./logs/xxl-job/jobhandler
#執行器紀錄檔檔案儲存天數 [選填] : 過期紀錄檔自動清理, 限制值大於等於3時生效; 否則, 如-1, 關閉自動清理功能;
logretentiondays: 30
@Slf4j
@Configuration
public class XxlJobConfig {
@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() {
log.info(">>>>>>>>>>> start 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);
return xxlJobSpringExecutor;
}
}
如果一切順利,將在控制檯看到如下輸出:
2023-10-09 11:36:23.162 INFO 15736 --- [ Thread-4] com.xxl.job.core.server.EmbedServer : >>>>>>>>>>> xxl-job remoting server start success, nettype = class com.xxl.job.core.server.EmbedServer, port = 9999
看到這個,說明執行器設定已生效,執行器已經順利和排程中心聯絡上了。
可以簡單的將任務分兩個步驟,第一在執行器中定義一個任務具體需要幹什麼,第二在排程中心觸發定義的任務
在業務微服務中建立
@Slf4j
@Component
public class DemoJob {
/**
* 簡單的job,排程器
*/
@XxlJob("job1")
public void job1() {
log.debug("do job1");
}
}
建立成功之後可以在執行器列表看到。圖片為編輯頁面,所以可以看到已經有機器地址了。
新增完畢之後啟動,如果一切順利,將可以在業務微服務中看到如下log:
2023-10-09 11:50:33.050 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob : do job1
2023-10-09 11:50:38.044 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob : do job1
2023-10-09 11:50:44.100 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob : do job1
2023-10-09 11:50:48.052 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob : do job1
2023-10-09 11:50:53.043 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob : do job1
每5s執行了一次任務
XxlJob註解有三個引數:
value:JobHandler的名稱,需要在執行器和排程中心保持一致
init:定時任務前置處理,僅在定時任務首次執行前執行一次
destory:定時任務後置處理,僅在定時任務銷燬的時候執行一次
這裡需要注意:
@Slf4j
@Component
public class DemoJob {
/**
* 建立帶前(後)置處理的任務
* Job方法新增註解 "@XxlJob(value="自定義jobhandler名稱", init = "JobHandler初始化方法", destroy = "JobHandler銷燬方法")",註解value值對應的是排程中心新建任務的JobHandler屬性的值。
* <p>
* 執行紀錄檔:需要通過 "XxlJobHelper.log" 列印執行紀錄檔;
* <p>
* 任務結果:預設任務結果為 "成功" 狀態,不需要主動設定;如有訴求,比如設定任務結果為失敗,可以通過 "XxlJobHelper.handleFail/handleSuccess" 自主設定任務結果;
*/
@XxlJob(value = "job2", init = "job2Init", destroy = "job2Destroy")
public void job2() throws InterruptedException {
LocalDateTime now = LocalDateTime.now();
XxlJobHelper.log("進入job2,time={}", now.toString());
log.debug("job2 - doSomething ...");
Thread.sleep(2000);
XxlJobHelper.log("離開job2,time={}", now.toString());
}
public void job2Init() {
log.debug("job2Init - doSomething ...");
}
public void job2Destroy() {
log.debug("job2Destroy - doSomething ...");
}
}
排程中心需要建立對應job2的任務並啟動。如果一切順利將在執行器控制檯看到如下log:
2023-10-09 13:31:44.104 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob : job2Init - doSomething ...
2023-10-09 13:31:44.110 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob : job2 - doSomething ...
2023-10-09 13:31:54.052 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob : job2 - doSomething ...
2023-10-09 13:32:04.053 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob : job2 - doSomething ...
2023-10-09 13:32:14.054 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob : job2 - doSomething ...
Disconnected from the target VM, address: '127.0.0.1:50819', transport: 'socket'
2023-10-09 13:32:21.606 INFO 35848 --- [ Thread-4] com.xxl.job.core.server.EmbedServer : >>>>>>>>>>> xxl-job remoting server stop.
2023-10-09 13:32:21.618 INFO 35848 --- [rRegistryThread] c.x.j.c.thread.ExecutorRegistryThread : >>>>>>>>>>> xxl-job registry-remove success, registryParam:RegistryParam{registryGroup='EXECUTOR', registryKey='xxljob-demo-service', registryValue='http://192.168.3.191:9999/'}, registryResult:ReturnT [code=200, msg=null, content=null]
2023-10-09 13:32:21.618 INFO 35848 --- [rRegistryThread] c.x.j.c.thread.ExecutorRegistryThread : >>>>>>>>>>> xxl-job, executor registry thread destroy.
2023-10-09 13:32:21.621 INFO 35848 --- [ionShutdownHook] com.xxl.job.core.server.EmbedServer : >>>>>>>>>>> xxl-job remoting server destroy success.
2023-10-09 13:32:21.622 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob : job2Destroy - doSomething ...
2023-10-09 13:32:21.622 INFO 35848 --- [7-1696829504104] com.xxl.job.core.thread.JobThread : >>>>>>>>>>> xxl-job JobThread stoped, hashCode:Thread[xxl-job, JobThread-27-1696829504104,10,main]
2023-10-09 13:32:21.623 INFO 35848 --- [FileCleanThread] c.x.j.core.thread.JobLogFileCleanThread : >>>>>>>>>>> xxl-job, executor JobLogFileCleanThread thread destroy.
通過log可以觀測到:
當兩個任務需要關聯觸發的時候可以使用父子任務的功能,當然了子任務還可以有子任務。
這種情況只需要啟動父任務,不需要啟動子任務,當父任務執行成功了,會觸發子任務的啟動。當父任務執行失敗了,不會觸發子任務的啟動。
/**
* 父任務
*/
@XxlJob("jobFather")
public void jobFather() {
// 建立一個新的亂數生成器
Random random = new Random();
// 生成一個0到100之間的隨機整數
int randomNumber = random.nextInt(101);
if (randomNumber % 2 == 0) {
log.debug("do - jobFather - success");
XxlJobHelper.handleSuccess();
} else {
log.debug("do - jobFather - fail");
XxlJobHelper.handleFail("呼叫XxlJobHelper.handleFail,排程中心就任務此任務執行失敗");
}
}
/**
* 子任務
*/
@XxlJob("jobChild")
public void jobChild() {
log.debug("do - jobChild");
}
2023-10-10 09:13:00.276 INFO 19228 --- [ Thread-4] com.xxl.job.core.server.EmbedServer : >>>>>>>>>>> xxl-job remoting server start success, nettype = class com.xxl.job.core.server.EmbedServer, port = 9999
2023-10-10 09:13:23.549 INFO 19228 --- [Pool-1699379094] c.xxl.job.core.executor.XxlJobExecutor : >>>>>>>>>>> xxl-job regist JobThread success, jobId:29, handler:com.xxl.job.core.handler.impl.MethodJobHandler@2b43f314[class com.ramble.xxljob.task.DemoJob#jobFather]
2023-10-10 09:13:23.552 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob : do - jobFather - fail
2023-10-10 09:13:28.495 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob : do - jobFather - fail
2023-10-10 09:13:34.500 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob : do - jobFather - success
2023-10-10 09:13:34.511 INFO 19228 --- [Pool-1699379094] c.xxl.job.core.executor.XxlJobExecutor : >>>>>>>>>>> xxl-job regist JobThread success, jobId:30, handler:com.xxl.job.core.handler.impl.MethodJobHandler@7e3d2ebd[class com.ramble.xxljob.task.DemoJob#jobChild]
2023-10-10 09:13:34.512 DEBUG 19228 --- [0-1696900414511] com.ramble.xxljob.task.DemoJob : do - jobChild
2023-10-10 09:13:38.494 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob : do - jobFather - fail
2023-10-10 09:13:43.526 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob : do - jobFather - success
2023-10-10 09:13:43.539 DEBUG 19228 --- [0-1696900414511] com.ramble.xxljob.task.DemoJob : do - jobChild
通過紀錄檔可以觀察到:
任務需要繫結到執行器,任務觸發排程時將會自動發現註冊成功的執行器, 實現任務自動發現功能; 另一方面也可以方便的進行任務分組。每個任務必須繫結一個執行器, 可在 "執行器管理" 進行設定
當執行器叢集部署時,提供豐富的路由策略,包括:
排程過於密集執行器來不及處理時的處理策略