一文帶你使用xxl-job定時任務
1.設計思想

將調度行為抽象形成“調度中心”公共平臺,而平臺自身并不承擔業務邏輯,“調度中心”負責發起調度請求。
將任務抽象成分散的JobHandler,交由“執行器”統一管理,“執行器”負責接收調度請求并執行對應的JobHandler中業務邏輯。
因此,“調度”和“任務”兩部分可以相互解耦,提高系統整體穩定性和擴展性;
2.系統組成
調度模塊(調度中心):
負責管理調度信息,按照調度配置發出調度請求,自身不承擔業務代碼。調度系統與任務解耦,提高了系統可用性和穩定性,同時調度系統性能不再受限于任務模塊;
支持可視化、簡單且動態的管理調度信息,包括任務新建,更新,刪除,GLUE開發和任務報警等,所有上述操作都會實時生效,同時支持監控調度結果以及執行日志,支持執行器Failover。
執行模塊(執行器):
負責接收調度請求并執行任務邏輯。任務模塊專注于任務的執行等操作,開發和維護更加簡單和高效;
接收“調度中心”的執行請求、終止請求和日志請求等。
架構圖
3.部署和與原有業務項目整合
1.初始化調度數據庫
位置:/xxl-job/doc/db/tables_xxl_job.sql
2.代碼結構
xxl-job-admin:調度中心(個人理解為服務端 提供定時任務服務調度) xxl-job-core:公共依賴 xxl-job-executor-samples:執行器Sample示例(選擇合適的版本執行器,可直接使用,也可以參考其并將現有項目改造成執行器) :xxl-job-executor-sample-springboot:Springboot版本,通過Springboot管理執行器,推薦這種方式;(個人理解就是負責保存定時任務數據 此模塊也業務代碼整合) :xxl-job-executor-sample-frameless:無框架版本;
3.配置調度中心(xxl-job-admin)
1.修改配置文件
老生常談,修改/xxl-job/xxl-job-admin/src/main/resources/application.properties配置文件。
### 調度中心JDBC鏈接:鏈接地址請保持和 2.1章節 所創建的調度數據庫的地址一致 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=root_pwd spring.datasource.driver-class-name=com.mysql.jdbc.Driver ### 報警郵箱 spring.mail.host=smtp.qq.com spring.mail.port=25 spring.mail.username=xxx@qq.com spring.mail.password=xxx spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true spring.mail.properties.mail.smtp.starttls.required=true spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory ### 調度中心通訊TOKEN [選填]:非空時啟用; xxl.job.accessToken= ### 調度中心國際化配置 [必填]: 默認為 "zh_CN"/中文簡體, 可選范圍為 "zh_CN"/中文簡體, "zh_TC"/中文繁體 and "en"/英文; xxl.job.i18n=zh_CN ## 調度線程池最大線程配置【必填】 xxl.job.triggerpool.fast.max=200 xxl.job.triggerpool.slow.max=100 ### 調度中心日志表數據保存天數 [必填]:過期日志自動清理;限制大于等于7時生效,否則, 如-1,關閉自動清理功能; xxl.job.logretentiondays=30
注意:因為定時任務頻率原因,日志特別大,請注意自動清除日志的配置 xxl.job.logretentiondays=1 不會生效。
2.Docker 鏡像方式搭建調度中心(可選)
下載鏡像
docker pull xuxueli/xxl-job-admin
執行容器
docker run -p 8080:8080 -v /tmp:/data/applogs --name xxl-job-admin -d xuxueli/xxl-job-admin:{指定版本} /** * 如需自定義 mysql 等配置,可通過 "-e PARAMS" 指定,參數格式 PARAMS="--key=value --key2=value2" ; * 配置項參考文件:/xxl-job/xxl-job-admin/src/main/resources/application.properties * 如需自定義 JVM內存參數 等配置,可通過 "-e JAVA_OPTS" 指定,參數格式 JAVA_OPTS="-Xmx512m" ; */ docker run -e PARAMS="--spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai" -p 8080:8080 -v /tmp:/data/applogs --name xxl-job-admin -d xuxueli/xxl-job-admin:{指定版本}
3.調度中心集群(可選):
調度中心支持集群部署,提升調度系統容災和可用性。調度中心集群部署時,幾點要求和建議:
DB配置保持一致;
集群機器時鐘保持一致(單機集群忽視);
建議:推薦通過nginx為調度中心集群做負載均衡,分配域名。調度中心訪問、執行器回調配置、調用API服務等操作均通過該域名進行。
4.配置部署“執行器項目”
“執行器”項目:xxl-job-executor-sample-springboot (提供多種版本執行器供選擇,現以 springboot 版本為例,可直接使用,也可以參考其并將現有項目改造成執行器) 作用:負責接收“調度中心”的調度并執行;可直接部署執行器,也可以將執行器集成到現有業務項目中。
可以將該項目加入為聚合工程,也可以獨立為服務,如果接入權限需要修改源碼。
1.maven依賴
確認pom文件中引入了 “xxl-job-core” 的maven依賴;
2.執行器配置
執行器配置,配置文件地址:
/xxl-job/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/application.properties
執行器配置,配置內容說明,如果沒有特別需求可以不修改:
### 調度中心部署跟地址 [選填]:如調度中心集群部署存在多個地址則用逗號分隔。執行器將會使用該地址進行"執行器心跳注冊"和"任務結果回調";為空則關閉自動注冊; xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin ### 執行器通訊TOKEN [選填]:非空時啟用; xxl.job.accessToken= ### 執行器AppName [選填]:執行器心跳注冊分組依據;為空則關閉自動注冊 xxl.job.executor.appname=xxl-job-executor-sample ### 執行器注冊 [選填]:優先使用該配置作為注冊地址,為空時使用內嵌服務 ”IP:PORT“ 作為注冊地址。從而更靈活的支持容器類型執行器動態IP和動態映射端口問題。 xxl.job.executor.address= ### 執行器IP [選填]:默認為空表示自動獲取IP,多網卡時可手動設置指定IP,該IP不會綁定Host僅作為通訊實用;地址信息用于 "執行器注冊" 和 "調度中心請求并觸發任務"; xxl.job.executor.ip= ### 執行器端口號 [選填]:小于等于0則自動獲取;默認端口為9999,單機部署多個執行器時,注意要配置不同執行器端口; xxl.job.executor.port=9999 ### 執行器運行日志文件存儲磁盤路徑 [選填] :需要對該路徑擁有讀寫權限;為空則使用默認路徑; xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler ### 執行器日志文件保存天數 [選填] : 過期日志自動清理, 限制值大于等于3時生效; 否則, 如-1, 關閉自動清理功能; xxl.job.executor.logretentiondays=30
3.執行器組件配置(無特別需求不需要更改)
執行器組件,配置文件地址:
/xxl-job/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/core/config/XxlJobConfig.java
該類就是獲取上文application.properties中的配置,并加入執行器:
@Bean public XxlJobSpringExecutor xxlJobExecutor() { logger.info(">>>>>>>>>>> xxl-job config init."); XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); xxlJobSpringExecutor.setAdminAddresses(adminAddresses); xxlJobSpringExecutor.setAppname(appname); xxlJobSpringExecutor.setIp(ip); xxlJobSpringExecutor.setPort(port); xxlJobSpringExecutor.setAccessToken(accessToken); xxlJobSpringExecutor.setLogPath(logPath); xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); return xxlJobSpringExecutor; }
4.部署執行器項目:
如果已經正確進行上述配置,可將執行器項目編譯打部署,系統提供多種執行器Sample示例項目,選擇其中一個即可,各自的部署方式如下。
xxl-job-executor-sample-springboot:項目編譯打包成springboot類型的可執行JAR包,命令啟動即可;
xxl-job-executor-sample-frameless:項目編譯打包成JAR包,命令啟動即可;
至此“執行器”項目已經部署結束。
5.執行器集群(可選):
執行器支持集群部署,提升調度系統可用性,同時提升任務處理能力。
執行器集群部署時,幾點要求和建議:
執行器回調地址(xxl.job.admin.addresses)需要保持一致;執行器根據該配置進行執行器自動注冊等操作。
同一個執行器集群內AppName(xxl.job.executor.appname)需要保持一致;調度中心根據該配置動態發現不同集群的在線執行器列表。
5.驗證
我們也可以使用源碼直接啟動,同時啟動以上兩個服務后。
訪問:http://localhost:8080/xxl-job-admin/jobinfo 賬號/密碼 admin/123456。
出現以下畫面代表成功。
4.xxl-job操作詳解
定時任務的數據方面的處理需要在xxl-job-executor-sample-springboot項目中(業務代碼),以下操作都是在這個項目中。
1.設置定時任務(重要)
1.BEAN模式(類形式,很少使用,且官網沒有示例。)
Bean模式任務,支持基于類的開發方式,每個任務對應一個Java類。
優點:不限制項目環境,兼容性好。即使是無框架項目,如main方法直接啟動的項目也可以提供支持,可以參考示例項目 “xxl-job-executor-sample-frameless”;
缺點:每個任務需要占用一個Java類,造成類的浪費;不支持自動掃描任務并注入到執行器容器,需要手動注入。
開發一個繼承自"com.xxl.job.core.handler.IJobHandler"的JobHandler類,實現其中任務方法。
手動通過如下方式注入到執行器容器。
XxlJobExecutor.registJobHandler("demoJobHandler", new DemoJobHandler());
2.BEAN模式(方法形式 常用!)
Bean模式任務,支持基于方法的開發方式,每個任務對應一個方法。
優點:每個任務只需要開發一個方法,并添加”@XxlJob”注解即可,更加方便、快速。支持自動掃描任務并注入到執行器容器。
缺點:要求Spring容器環境;
基于方法開發的任務,底層會生成JobHandler代理,和基于類的方式一樣,任務也會以JobHandler的形式存在于執行器任務容器中。
以下是bean模式例子
任務開發:在Spring Bean實例中,開發Job方法;
注解配置:為Job方法添加注解 “@XxlJob(value=“自定義jobhandler名稱”, init = “JobHandler初始化方法”, destroy = “JobHandler銷毀方法”)”,注解value值對應的是調度中心新建任務的JobHandler屬性的值。
執行日志:需要通過 “XxlJobHelper.log” 打印執行日志;
任務結果:默認任務結果為 “成功” 狀態,不需要主動設置;如有訴求,比如設置任務結果為失敗,可以通過 “XxlJobHelper.handleFail/handleSuccess” 自主設置任務結果;
// 可參考Sample示例執行器中的 "com.xxl.job.executor.service.jobhandler.SampleXxlJob" ,如下: @XxlJob("demoJobHandler") public void demoJobHandler() throws Exception { XxlJobHelper.log("XXL-JOB, Hello World."); }
配置項說明:
基礎配置: - 執行器:任務的綁定的執行器,任務觸發調度時將會自動發現注冊成功的執行器, 實現任務自動發現功能; 另一方面也可以方便的進行任務分組。每個任務必須綁定一個執行器, 可在 "執行器管理" 進行設置; - 任務描述:任務的描述信息,便于任務管理; - 負責人:任務的負責人; - 報警郵件:任務調度失敗時郵件通知的郵箱地址,支持配置多郵箱地址,配置多個郵箱地址時用逗號分隔; 觸發配置: - 調度類型: 無:該類型不會主動觸發調度; CRON:該類型將會通過CRON,觸發任務調度; 固定速度:該類型將會以固定速度,觸發任務調度;按照固定的間隔時間,周期性觸發; 固定延遲:該類型將會以固定延遲,觸發任務調度;按照固定的延遲時間,從上次調度結束后開始計算延遲時間,到達延遲時間后觸發下次調度; - CRON:觸發任務執行的Cron表達式; - 固定速度:固件速度的時間間隔,單位為秒; - 固定延遲:固件延遲的時間間隔,單位為秒; 任務配置: - 運行模式: BEAN模式:任務以JobHandler方式維護在執行器端;需要結合 "JobHandler" 屬性匹配執行器中任務; GLUE模式(Java):任務以源碼方式維護在調度中心;該模式的任務實際上是一段繼承自IJobHandler的Java類代碼并 "groovy" 源碼方式維護,它在執行器項目中運行,可使用@Resource/@Autowire注入執行器里中的其他服務; GLUE模式(Shell):任務以源碼方式維護在調度中心;該模式的任務實際上是一段 "shell" 腳本; GLUE模式(Python):任務以源碼方式維護在調度中心;該模式的任務實際上是一段 "python" 腳本; GLUE模式(PHP):任務以源碼方式維護在調度中心;該模式的任務實際上是一段 "php" 腳本; GLUE模式(NodeJS):任務以源碼方式維護在調度中心;該模式的任務實際上是一段 "nodejs" 腳本; GLUE模式(PowerShell):任務以源碼方式維護在調度中心;該模式的任務實際上是一段 "PowerShell" 腳本; - JobHandler:運行模式為 "BEAN模式" 時生效,對應執行器中新開發的JobHandler類“@JobHandler”注解自定義的value值; - 執行參數:任務執行所需的參數; 高級配置: - 路由策略:當執行器集群部署時,提供豐富的路由策略,包括; FIRST(第一個):固定選擇第一個機器; LAST(最后一個):固定選擇最后一個機器; ROUND(輪詢):; RANDOM(隨機):隨機選擇在線的機器; CONSISTENT_HASH(一致性HASH):每個任務按照Hash算法固定選擇某一臺機器,且所有任務均勻散列在不同機器上。 LEAST_FREQUENTLY_USED(最不經常使用):使用頻率最低的機器優先被選舉; LEAST_RECENTLY_USED(最近最久未使用):最久未使用的機器優先被選舉; FAILOVER(故障轉移):按照順序依次進行心跳檢測,第一個心跳檢測成功的機器選定為目標執行器并發起調度; BUSYOVER(忙碌轉移):按照順序依次進行空閑檢測,第一個空閑檢測成功的機器選定為目標執行器并發起調度; SHARDING_BROADCAST(分片廣播):廣播觸發對應集群中所有機器執行一次任務,同時系統自動傳遞分片參數;可根據分片參數開發分片任務; - 子任務:每個任務都擁有一個唯一的任務ID(任務ID可以從任務列表獲取),當本任務執行結束并且執行成功時,將會觸發子任務ID所對應的任務的一次主動調度。 - 調度過期策略: - 忽略:調度過期后,忽略過期的任務,從當前時間開始重新計算下次觸發時間; - 立即執行一次:調度過期后,立即執行一次,并從當前時間開始重新計算下次觸發時間; - 阻塞處理策略:調度過于密集執行器來不及處理時的處理策略; 單機串行(默認):調度請求進入單機執行器后,調度請求進入FIFO隊列并以串行方式運行; 丟棄后續調度:調度請求進入單機執行器后,發現執行器存在運行的調度任務,本次請求將會被丟棄并標記為失敗; 覆蓋之前調度:調度請求進入單機執行器后,發現執行器存在運行的調度任務,將會終止運行中的調度任務并清空隊列,然后運行本地調度任務; - 任務超時時間:支持自定義任務超時時間,任務運行超時將會主動中斷任務; - 失敗重試次數;支持自定義任務失敗重試次數,當任務失敗時將會按照預設的失敗重試次數主動進行重試;
上文中JobHandler具體解釋,很少用。
JobHandler:為方便用戶參考與快速實用,示例執行器內原生提供多個Bean模式任務Handler,可以直接配置實用,如下:
demoJobHandler:簡單示例任務,任務內部模擬耗時任務邏輯,用戶可在線體驗Rolling Log等功能; shardingJobHandler:分片示例任務,任務內部模擬處理分片參數,可參考熟悉分片任務; httpJobHandler:通用HTTP任務Handler;業務方只需要提供HTTP鏈接等信息即可,不限制語言、平臺。示例任務入參如下: url: http://www.xxx.com method: get 或 post data: post-data commandJobHandler:通用命令行任務Handler;業務方只需要提供命令行即可;如 “pwd”命令;
上文示例
@Component public class SampleXxlJob { private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class); /** * 1、簡單任務示例(Bean模式) */ @XxlJob("demoJobHandler") public void demoJobHandler() throws Exception { XxlJobHelper.log("XXL-JOB, Hello World."); System.out.println("11"); for (int i = 0; i < 5; i++) { XxlJobHelper.log("beat at:" + i); } // default success } /** * 2、分片廣播任務 */ @XxlJob("shardingJobHandler") public void shardingJobHandler() throws Exception { // 分片參數 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 { 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 { // 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("XXL-JOB, Hello World."); } public void init(){ logger.info("init"); } public void destroy(){ logger.info("destory"); } }
3.GLUE(Java)
可以在以下頁面添加執行內容。
可以在以下頁面添加執行內容。
2.其他操作
定時任務添加的步驟最重要,上文詳細描述了。
其他還有執行一次,暫停,刪除,編輯,啟動(定時任務),查看日志等功能選擇按鈕執行即可
5.調度模塊剖析(概念,選讀)
1.quartz的不足
Quartz作為開源作業調度中的佼佼者,是作業調度的首選。但是集群環境中Quartz采用API的方式對任務進行管理,從而可以避免上述問題,但是同樣存在以下問題:
調用API的的方式操作任務,不人性化;
需要持久化業務QuartzJobBean到底層數據表中,系統侵入性相當嚴重。
調度邏輯和QuartzJobBean耦合在同一個項目中,這將導致一個問題,在調度任務數量逐漸增多,同時調度任務邏輯逐漸加重的情況下,此時調度系統的性能將大大受限于業務;
quartz底層以“搶占式”獲取DB鎖并由搶占成功節點負責運行任務,會導致節點負載懸殊非常大;而XXL-JOB通過執行器實現“協同分配式”運行任務,充分發揮集群優勢,負載各節點均衡。
XXL-JOB彌補了quartz的上述不足之處。
2.研調度模塊
XXL-JOB最終選擇自研調度組件(早期調度組件基于Quartz);一方面是為了精簡系統降低冗余依賴,另一方面是為了提供系統的可控度與穩定性;
XXL-JOB中“調度模塊”和“任務模塊”完全解耦,調度模塊進行任務調度時,將會解析不同的任務參數發起遠程調用,調用各自的遠程執行器服務。這種調用模型類似RPC調用,調度中心提供調用代理的功能,而執行器提供遠程服務的功能。
3.調度中心HA(集群)
基于數據庫的集群方案,數據庫選用Mysql;集群分布式并發環境中進行定時任務調度時,會在各個節點會上報任務,存到數據庫中,執行時會從數據庫中取出觸發器來執行,如果觸發器的名稱和執行時間相同,則只有一個節點去執行此任務
4.調度線程池
調度采用線程池方式實現,避免單線程因阻塞而引起任務調度延遲。
5.過期處理策略
任務調度錯過觸發時間時的處理策略:
可能原因:服務重啟;調度線程被阻塞,線程被耗盡;上次調度持續阻塞,下次調度被錯過;
處理策略:
1. 過期超5s:本次忽略,當前時間開始計算下次觸發時間
2. 過期5s內:立即觸發一次,當前時間開始計算下次觸發時間
6.日志回調服務
調度模塊的“調度中心”作為Web服務部署時,一方面承擔調度中心功能,另一方面也為執行器提供API服務。
調度中心提供的”日志回調服務API服務”代碼位置如下:
xxl-job-admin#com.xxl.job.admin.controller.JobApiController.callback
“執行器”在接收到任務執行請求后,執行任務,在執行結束之后會將執行結果回調通知“調度中心”:
7.調度日志
調度中心每次進行任務調度,都會記錄一條任務日志,任務日志主要包括以下三部分內容:
任務信息:包括“執行器地址”、“JobHandler”和“執行參數”等屬性,點擊任務ID按鈕可查看,根據這些參數,可以精確的定位任務執行的具體機器和任務代碼;
調度信息:包括“調度時間”、“調度結果”和“調度日志”等,根據這些參數,可以了解“調度中心”發起調度請求時具體情況。
執行信息:包括“執行時間”、“執行結果”和“執行日志”等,根據這些參數,可以了解在“執行器”端任務執行的具體情況;
調度日志,針對單次調度,屬性說明如下:
執行器地址:任務執行的機器地址;
JobHandler:Bean模式表示任務執行的JobHandler名稱;
任務參數:任務執行的入參;
調度時間:調度中心,發起調度的時間;
調度結果:調度中心,發起調度的結果,SUCCESS或FAIL;
調度備注:調度中心,發起調度的備注信息,如地址心跳檢測日志等;
執行時間:執行器,任務執行結束后回調的時間;
執行結果:執行器,任務執行的結果,SUCCESS或FAIL;
執行備注:執行器,任務執行的備注信息,如異常日志等;
執行日志:任務執行過程中,業務代碼中打印的完整執行日志;
8.全異步化 & 輕量級
全異步化設計:XXL-JOB系統中業務邏輯在遠程執行器執行,觸發流程全異步化設計。相比直接在調度中心內部執行業務邏輯,極大的降低了調度線程占用時間;
1. 異步調度:調度中心每次任務觸發時僅發送一次調度請求,該調度請求首先推送“異步調度隊列”,然后異步推送給遠程執行器。
2. 異步執行:執行器會將請求存入“異步執行隊列”并且立即響應調度中心,異步運行。
輕量級設計:XXL-JOB調度中心中每個JOB邏輯非常 “輕”,在全異步化的基礎上,單個JOB一次運行平均耗時基本在 “10ms” 之內(基本為一次請求的網絡開銷);因此,可以保證使用有限的線程支撐大量的JOB并發運行;
得益于上述兩點優化,理論上默認配置下的調度中心,單機能夠支撐 5000 任務并發運行穩定運行;
實際場景中,由于調度中心與執行器網絡ping延遲不同、DB讀寫耗時不同、任務調度密集程度不同,會導致任務量上限會上下波動。
如若需要支撐更多的任務量,可以通過 “調大調度線程數” 、”降低調度中心與執行器ping延遲” 和 “提升機器配置” 幾種方式優化。
9.Bean模式” 運行原理
原理:每個Bean模式任務都是一個Spring的Bean類實例,它被維護在“執行器”項目的Spring容器中。任務類需要加“@JobHandler(value=”名稱”)”注解,因為“執行器”會根據該注解識別Spring容器中的任務。任務類需要繼承統一接口“IJobHandler”,任務邏輯在execute方法中開發,因為“執行器”在接收到調度中心的調度請求時,將會調用“IJobHandler”的execute方法,執行任務邏輯。
10.“GLUE模式(Java)” 運行原理
原理:每個 “GLUE模式(Java)” 任務的代碼,實際上是“一個繼承自“IJobHandler”的實現類的類代碼”,“執行器”接收到“調度中心”的調度請求時,會通過Groovy類加載器加載此代碼,實例化成Java對象,同時注入此代碼中聲明的Spring服務(請確保Glue代碼中的服務和類引用在“執行器”項目中存在),然后調用該對象的execute方法,執行任務邏輯。
11.執行器
執行器實際上是一個內嵌的Server,默認端口9999(配置項:xxl.job.executor.port)。
在項目啟動時,執行器會通過“@JobHandler”識別Spring容器中“Bean模式任務”,以注解的value屬性為key管理起來。
“執行器”接收到“調度中心”的調度請求時,如果任務類型為“Bean模式”,將會匹配Spring容器中的“Bean模式任務”,然后調用其execute方法,執行任務邏輯。如果任務類型為“GLUE模式”,將會加載GLue代碼,實例化Java對象,注入依賴的Spring服務(注意:Glue代碼中注入的Spring服務,必須存在與該“執行器”項目的Spring容器中),然后調用execute方法,執行任務邏輯。
12.一次完整的任務調度通訊流程
1、“調度中心”向“執行器”發送http調度請求: “執行器”中接收請求的服務,實際上是一臺內嵌Server,默認端口9999;
2、“執行器”執行任務邏輯;
3、“執行器”http回調“調度中心”調度結果: “調度中心”中接收回調的服務,是針對執行器開放一套API服務;
13.分片廣播 & 動態分片
執行器集群部署時,任務路由策略選擇”分片廣播”情況下,一次任務調度將會廣播觸發對應集群中所有執行器執行一次任務,同時系統自動傳遞分片參數;可根據分片參數開發分片任務;
“分片廣播” 以執行器為維度進行分片,支持動態擴容執行器集群從而動態增加分片數量,協同進行業務處理;在進行大數據量業務操作時可顯著提升任務處理能力和速度。
“分片廣播” 和普通任務開發流程一致,不同之處在于可以獲取分片參數,獲取分片參數進行分片業務處理。
該特性適用場景如:
分片任務場景:10個執行器的集群來處理10w條數據,每臺機器只需要處理1w條數據,耗時降低10倍;
廣播任務場景:廣播執行器機器運行shell腳本、廣播集群節點進行緩存更新等
Java Spring
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。