文章目錄
- 1 概述
- 2 特性
- 3 架構設計
-
- 3.1設計思想
- 3.2 系統組成
- 3.3架構圖
- 3.4 高可用
-
- 3.4.1 排程中心的高可用
- 3.4.2 執行器的高可用
- 4 搭建排程中心
-
- 4.1 克隆源碼
- 4.2 初始化 XXL-JOB 表結構
- 修改配置檔案
- 4.4 修改日志配置檔案
- 4.5 IDEA 啟動排程中心
- 4.6 編譯源碼
- 4.7 指令行啟動排程中心
- 4.8 搭建叢集
- 5 搭建執行器
-
- 5.1 引入依賴
- 5.2 應用配置檔案
- 5.3 XxlJobConfiguration
- 5.4 DemoJob
- 5.6 新增執行器
- 5.7 建立任務
1 概述
XXL-JOB 是一個輕量級分布式任務排程平台,其核心設計目标是開發迅速、學習簡單、輕量級、易擴充。
從它登記的接入公司清單 ,可以看到拍拍貸、優信二手車、京東、哈啰出行等知名的網際網路公司正在使用中。是以,胖友們是可以放心大膽的在項目中使用。
2 特性
XXL-JOB 提供了 35 點特性清單
3 架構設計
3.1設計思想
- 将排程行為抽象形成“排程中心”公共平台,而平台自身并不承擔業務邏輯,“排程中心”負責發起排程請求。
- 将任務抽象成分散的
,交由“執行器”統一管理,“執行器”負責接收排程請求并執行對應的 JJobHandler
中 業務邏輯。obHandler
是以,“排程”和“任務”兩部分可以互相解耦,提高系統整體穩定性和擴充性。
如果對分布式任務排程平台有一定了解的話,如果從排程系統的角度來看,可以分成兩類:
- 中心化: 排程中心和執行器分離,排程中心統一排程,通知某個執行器處理任務。
- 去中心化:排程中心和執行器一體化,自己排程自己執行處理任務。
如此可知 XXL-Job 屬于中心化的任務排程平台。目前采用這種方案的還有:
- 鍊家的 kob
- 美團的 Crane(暫未開源)
去中心化的任務排程平台,目前有:
- Elastic Job
- 唯品會的 Saturn
- Quartz 基于資料庫的叢集方案
- 淘寶的 TBSchedule(暫停更新,隻能使用阿裡雲 SchedulerX 服務)
3.2 系統組成
整個 XXL-JOB 系統,由排程中心和執行器兩個角色組成,分别處于不同的程序中。
排程中心:
- 負責管理排程資訊,按照排程配置發出排程請求,自身不承擔業務代碼。
- 排程系統與任務解耦,提高了系統可用性和穩定性,同時排程系統性能不再受限于任務子產品。
- 支援可視化、簡單且動态的管理排程資訊,包括任務建立,更新,删除, GLUE 開發和任務報警等,所有上述操作都會實時生效。
- 支援監控排程結果以及執行日志,支援執行器 Failover 。
執行器:
- 負責接收排程請求并執行任務邏輯。任務子產品專注于任務的執行等操作,開發和維護更加簡單和高效。
- 接收“排程中心”的執行請求、終止請求和日志請求等。
一般來說,XXL-JOB 執行器可以内嵌到應用服務裡。例如說,一個提供 Restful API 的 Spring Boot 項目中,引入 xxl-job-core 依賴,同時也作為一個 XXL-JOB 執行器。本質上,每次 Restful API 是請求任務,而每次任務排程是定時任務。(?)
3.3架構圖
注意,【】 中填寫排程中心和執行器;[] 中填寫元件名。
注意,左邊是排程中心,右邊是執行器。
【執行器】:[注冊線程] 根據配置的【排程中心】的位址,自動注冊到【排程中心】。
【排程中心】:達到任務觸發條件,【排程中心】下發任務給【執行器】。
【執行器】:基于 [任務線程池] 執行任務,并把執行結果放入 [記憶體隊列] 中、把 [執行日志] 寫入 Log 日志檔案中。
【執行器】:[回調線程] 消費 [記憶體隊列] 中的排程結果,主動上報給【排程中心】。
當使用者在【排程中心】檢視 [Rolling 任務日志],【排程中心】請求【執行器】,【執行器】讀取 Log 日志檔案并傳回日志詳情。
3.4 高可用
XXL-JOB 的高可用,需要考慮排程中心的高可用、以及執行器的高可用。
注意,雖然說 XXL-JOB 執行的是背景任務,即使挂掉,使用者的感覺度也比較低,但是考慮高可用是一種良好的習慣,在高性能之前請做好高可用。
3.4.1 排程中心的高可用
排程中心支援多節點部署,基于資料庫行鎖,保證觸發器的名稱和執行時間相同,則隻且僅有一個排程中心節點去下發任務給執行器。
核心代碼可見 XXL-JOB 的
JobScheduleHelper#start()
方法:
// JobScheduleHelper.java
// 獲得行鎖
conn = XxlJobAdminConfig.getAdminConfig().getDataSource().getConnection();
connAutoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
preparedStatement = conn.prepareStatement( "select * from xxl_job_lock where lock_name = 'schedule_lock' for update" );
preparedStatement.execute();
// ...觸發任務排程
// 事務送出
conn.commit();
3.4.2 執行器的高可用
執行器支援多節點部署,通過排程中心選擇其中的執行器,下發任務來執行。
當任務”路由政策”選擇”故障轉移(FAILOVER)”時,當排程中心每次發起排程請求時,會按照順序對執行器發出心跳檢測請求,第一個檢測為存活狀态的執行器将會被標明并發送排程請求。
-
路由政策
——排程中心基于路由政策,選擇一個執行器節點下發任務,進而讓執行器執行任務。XXL-JOB 提供了如下路由政策保證任務排程高可用:
- 忙碌轉移(BUSYOVER)政策:當排程中心每次發起排程請求時,會按照順序對執行器發出空閑檢測請求,第一個檢測為空閑狀态的執行器将會被標明并發送排程請求。具體代碼,見 ExecutorRouteFailover 類。
- 故障轉移(FAILOVER)政策:當排程中心每次發起排程請求時,會按照順序對執行器發出心跳檢測請求,第一個檢測為存活狀态的執行器将會被標明并發送排程請求。具體代碼,見 ExecutorRouteFailover 類。
-
阻塞處理政策
——當排程過于密集時,執行器來不及處理時,則當執行器節點存在多個相同任務編号的任務未執行完成,則需要基于阻塞處理政策對任務進行取舍:
- 單機串行(預設):排程請求進入單機執行器後,排程請求進入 FIFO 隊列并以串行方式運作。
- 丢棄後續排程:排程請求進入單機執行器後,發現執行器存在運作的排程任務,本次請求将會被丢棄并标記為失敗。
- 覆寫之前排程:排程請求進入單機執行器後,發現執行器存在運作的排程任務,将會終止運作中的排程任務并清空隊列,然後運作本地排程任務。
4 搭建排程中心
本小節,我們來搭建一個排程中心。XXL-JOB 暫未提供直接直接啟動的 jar 包,是以需要自己編譯源碼。
考慮到降低大家的學習成本,我們使用 IDEA 進行操作。
4.1 克隆源碼
用 IDEA ,從碼雲 https://gitee.com/xuxueli0323/xxl-job 克隆源碼,或者從 XXL-JOB Releases中克隆。
克隆完成後,耐心等待下載下傳完依賴。完成後,整體項目結構如下:
-
子產品:XXL-JOB 核心。後續我們在編寫執行器時,會引入該子產品。xxl-job-core
-
子產品:排程中心。xxl-job-admin
-
子產品:提供了在 Spring、Spring Boot、JFinal、Nutz 等架構下的使用示例。xxl-job-executor-samples
這裡,我們需要編譯的主要是
xxl-job-admin
子產品,即排程中心。
4.2 初始化 XXL-JOB 表結構
在
doc/db/tables_xxl_job.sql
位址,是 XXL-JOB 表結構的初始化腳本。我們需要在資料庫中執行該腳本,完成初始化 XXL-JOB 表結構。如下圖所示:XXL-JOB 表結構
- xxl_job_lock:任務排程鎖表;
- xxl_job_group:執行器資訊表,維護任務執行器資訊;
- xxl_job_info:排程擴充資訊表: 用于儲存 XXL-JOB 排程任務的擴充資訊,如任務分組、任務名、機器位址、執行器、執行入參和報警郵件等等;
- xxl_job_log:排程日志表: 用于儲存 XXL-JOB 任務排程的曆史資訊,如排程結果、執行結果、排程入參、排程機器和執行器等等;
- xxl_job_log_report:排程日志報表:使用者存儲 XXL-JOB 任務排程日志的報表,排程中心報表功能頁面會用到;
- xxl_job_logglue:任務GLUE日志:用于儲存 GLUE 更新曆史,用于支援 GLUE 的版本回溯功能;
- xxl_job_registry:執行器系統資料庫,維護線上的執行器和排程中心機器位址資訊;
- xxl_job_user:系統使用者表;
自 XXL-JOB 2.1.0 Release 版本,去除對 Quartz 的依賴,是以我們就看不到 Quartz 相關的表哈。
修改配置檔案
打開
xxl-job-admin
子產品,修改
src/main/resources/application.properties
配置檔案。如下:
### web # Web 伺服器
server.port=8080
server.context-path=/xxl-job-admin
### actuator
management.context-path=/actuator
management.health.mail.enabled=false
### resources
spring.mvc.static-path-pattern=/static/**
spring.resources.static-locations=classpath:/static/
### freemarker
spring.freemarker.templateLoaderPath=classpath:/templates/
spring.freemarker.suffix=.ftl
spring.freemarker.charset=UTF-8
spring.freemarker.request-context-attribute=request
spring.freemarker.settings.number_format=0.##########
### mybatis
mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml
### xxl-job, datasource 排程中心 JDBC 連結
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?Unicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root_pwd
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
spring.datasource.tomcat.max-wait=10000
spring.datasource.tomcat.max-active=30
spring.datasource.tomcat.test-on-borrow=true
spring.datasource.tomcat.validation-query=SELECT 1
spring.datasource.tomcat.validation-interval=30000
### xxl-job email 報警郵箱
spring.mail.host=smtp.qq.com
spring.mail.port=25
[email protected]
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
### xxl-job, access token 排程中心通訊TOKEN [選填]:非空時啟用;排程中心國際化配置 [選填]: 預設為空,表示中文; "en" 表示英文;
xxl.job.accessToken=
### xxl-job, i18n (default empty as chinese, "en" as english)
xxl.job.i18n=
## xxl-job, triggerpool max size 排程線程池最大線程配置
xxl.job.triggerpool.fast.max=200
xxl.job.triggerpool.slow.max=100
### xxl-job, log retention days 排程中心日志表資料儲存天數 [必填]:過期日志自動清理;限制大于等于7時生效,否則, 如-1,關閉自動清理功能;
xxl.job.logretentiondays=30
可以看到 XXL-JOB 使用了 Spring Boot ,配置項比較多,說下必須要改的項:
-
:XXL-JOB 排程中心的伺服器位址。可以根據自己的需要,修改該端口。server.port
-
:XXL-JOB 排程中心的資料源位址,必須修改成自己準備提供給 XXL-JOB 的資料庫位址。spring.datasource
-
:報警郵箱,生産環境下必須配置,不然定時任務執行報錯都不知道,簡直要命。😈 一般來說下,建議有時間的胖友,修改下 XXL-JOB 的源碼,把釘釘告警接入。spring.mail
-
:排程中心通訊令牌,建議填寫。雖然說,内網一般很安全,但是以防萬一,并且又沒啥成本,直接給整上。xxl.job.accessToken
4.4 修改日志配置檔案
打開
xxl-job-admin
子產品,修改
src/main/resources/logback.xml
配置檔案。如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<property name="log.path" value="/data/applogs/xxl-job/xxl-job-admin.log"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="console"/>
<appender-ref ref="file"/>
</root>
</configuration>
預設情況下,日志輸出的位址是
/data/applogs/xxl-job/xxl-job-admin.log
。可以根據自己的需要,進行調整。
4.5 IDEA 啟動排程中心
在開始編譯源碼之前,我們先直接使用
XxlJobAdminApplication
類,運作啟動排程中心。這樣,避免我們後面編譯源碼,進行打包查出來的 jar 包,結果配置檔案不對的尴尬。
當看到如下日志,代表啟動成功:
啟動成功後,浏覽器 http://127.0.0.1:8080/xxl-job-admin 位址,并使用預設 “admin/123456” 進行登入。如果登入成功,說明我們已經配置正确啦。
4.6 編譯源碼
使用 Maven 打包指定
xxl-job-admin
子產品,打包完成後,在
xxl-job-admin/target/xxl-job-admin-2.1.2-SNAPSHOT.jar
位址下,就是我們要啟動的 XXL-JOB 排程中心的 jar 包。
4.7 指令行啟動排程中心
# 啟動排程中心
$ jar -jar xxl-job-admin-2.1.2-SNAPSHOT.jar
啟動成功後,浏覽器 http://127.0.0.1:8080/xxl-job-admin 位址,并使用預設 “admin/123456” 進行登入。如果登入成功,說明我們已經配置正确啦。
另外,啟動完成之後,記得右上角,修改下管理者的密碼。
4.8 搭建叢集
在生産環境下,一定要部署 XXL-JOB 排程中心的叢集,提升排程系統容災和可用性。
排程中心叢集部署時,幾點要求和建議:
- DB 配置保持一緻;
- 叢集機器時鐘保持一緻(單機叢集忽視);
- 推薦通過 Nginx 為排程中心叢集做負載均衡,配置設定域名。排程中心通路、執行器回調配置、調用 API 服務等操作均通過該域名進行。
另外,如果胖友想要使用 Docker 鏡像方式搭建排程中心,可以自行參看 XXL-JOB 的官方文檔。
5 搭建執行器
5.1 引入依賴
建立一個springboot項目,在
pom.xml
檔案中,引入相關依賴。注意這裡的依賴版本要與排程中心的版本一緻!!
<!-- XXL-JOB 相關依賴 -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.1</version>
</dependency>
5.2 應用配置檔案
在 application.yml 中,添加 Quartz 的配置,如下:
server:
port: 9090 #指定一個端口,避免和 XXL-JOB 排程中心的端口沖突。僅僅測試之用
# xxl-job
xxl:
job:
admin:
addresses: http://xxxx.1:8080/xxl-job-admin # 排程中心部署跟位址(不能使用127.0.0.1) [選填]:如排程中心叢集部署存在多個位址則用逗号分隔。執行器将會使用該位址進行"執行器心跳注冊"和"任務結果回調";為空則關閉自動注冊;
executor:
appname: task-xxl-job # 執行器 AppName [選填]:執行器心跳注冊分組依據;為空則關閉自動注冊
ip: # 執行器IP [選填]:預設為空表示自動擷取IP,多網卡時可手動設定指定IP,該IP不會綁定Host僅作為通訊實用;位址資訊用于 "執行器注冊" 和 "排程中心請求并觸發任務";
port: 6666 # ### 執行器端口号 [選填]:小于等于0則自動擷取;預設端口為9999,單機部署多個執行器時,注意要配置不同執行器端口;
logpath: /logs # 執行器運作日志檔案存儲磁盤路徑 [選填] :需要對該路徑擁有讀寫權限;為空則使用預設路徑;
logretentiondays: 30 # 執行器日志檔案儲存天數 [選填] : 過期日志自動清理, 限制值大于等于3時生效; 否則, 如-1, 關閉自動清理功能;
accessToken: # 執行器通訊TOKEN [選填]:非空時啟用;
5.3 XxlJobConfiguration
在
Application
同級目錄下建立
config
包,并建立 DataSourceConfiguration 類,配置 XXL-JOB 執行器。代碼如下:
@Configuration
public class XxlJobConfiguration {
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.executor.appname}")
private String appName;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
// 建立 XxlJobSpringExecutor 執行器
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;
}
}
在
xxlJobExecutor()
方法,建立了 Spring 容器下的 XXL-JOB 執行器 Bean 對象。要注意,方法上添加了的
@Bean
注解,配置了啟動和銷毀方法。
5.4 DemoJob
在
Application
同級目錄下建立
job
包,并建立 DemoJob 類,示例定時任務類。代碼如下:
在xxl-job 2.2版本之前可以使用
@JobHandler
并繼承
IJobHandler
類
@Component
@JobHandler("demoJob")
public class DemoJob extends IJobHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
private final AtomicInteger counts = new AtomicInteger();
@Override
public ReturnT<String> execute(String param) throws Exception {
// 列印日志
logger.info("[execute][定時第 ({}) 次執行]", counts.incrementAndGet());
// 傳回執行成功
return ReturnT.SUCCESS;
}
}
在xxl-job 2.2版本之後,使用
@XxlJob
替代了原方法
@Component
public class DemoJob {
private Logger logger = LoggerFactory.getLogger(getClass());
//
private final AtomicInteger counts = new AtomicInteger();
@XxlJob("demoJobHandler")
public ReturnT<String> demoJobHandler(String param) throws Exception {
logger.info("[execute][定時第 ({}) 次執行]", counts.incrementAndGet());
return ReturnT.SUCCESS;
}
}
此時運作項目并不會成功,因為我們還需要在排程中心先注冊執行器
5.6 新增執行器
浏覽器打開 http://127.0.0.1:8080/xxl-job-admin/jobgroup 位址,即「執行器管理」菜單。如下圖:
新增執行器:
在
xxl-job 2.3.1
版本中,使用自動注冊的機器位址不會加上
http://
而會導緻之後調用任務失敗,這裡推薦手動配置一下
5.7 建立任務
浏覽器打開 http://127.0.0.1:8080/xxl-job-admin/jobinfo 位址,即「任務管理」菜單。如下圖:
點選最右邊的「新增」按鈕,彈出「新增」界面。如下圖:
填寫完記得切換執行器
點選 “demoJob” 任務的「操作」按鈕,選擇「啟動」,确認後,該 “demoJob” 任務的狀态就變成了 RUNNING 。
此時,我們打開執行器的 IDE 界面,可以看到 DemoJob 已經在每秒鐘執行一次了。