為獲得更好的閱讀體驗,請訪問原文:傳送門
一、分散式任務排程概述
什麼是任務排程平臺
任務排程是指基於給定的時間點,給定的時間間隔又或者給定執行次數自動的執行任務。我們可以思考一下在以下場景中,我們應該怎麼實現:
- 支付系統每天凌晨 1 點,進行一天清算,每月 1 號進行上個月清算;
- 電商整點搶購,商品價格8點整開始優惠
- 12306 購票系統,超過 30 分鐘沒有成功支付訂單的,進行回收處理
為什麼需要任務排程平臺
定時任務是程式設計師不可避免的話題,很多業務場景需要我們某一特定的時刻去做某件任務。一般來說,系統可以使用訊息傳遞代替部分定時任務(比如商品成功發貨後,需要向客戶傳送簡訊提醒),兩者有很多相似之處,一些場景下也可以相互替換,但是有一些不能:
- 時間驅動/ 事件驅動: 內部系統一般可以通過事件來驅動,但如果涉及到外部系統,則只能使用時間驅動。如爬取外部網站價格,每小時爬一次。
- 批量處理/ 逐條處理: 批量處理堆積的資料更加高效,在不需要實時性的情況下比訊息中介軟體更有優勢。而且有的業務邏輯只能批量處理,如移動每個月結算我們的花費。
- 實時性/ 非實時性: 訊息中介軟體能夠做到實時處理資料,但是有些情況下並不需要實時,比如:vip 升級。
- 系統內部/ 系統解耦: 定時任務排程一般是在系統內部,而訊息中介軟體可用於兩個系統間
並且對於分散式系統來說,如果處理不當,會存在同一系統不同節點之間定時任務相互影響的問題,再考慮上監控、日誌、資訊皮膚,加上不同系統之間管理維護的問題,自己實現一套的成本又上來了..所以我們可以考慮一些比較成熟的任務排程平臺來使用。
任務排程框架選型
Java 領域主要分散式排程系統如下:
- xxl-job:是一個輕量級分散式任務排程平臺,其核心設計目標是開發迅速、學習簡單、輕量級、易擴充套件 。
- Elastic-Job: 噹噹開源的分散式排程解決方案,由兩個相互獨立的子專案Elastic-Job-Lite和Elastic-Job-Cloud組成;Elastic-Job-Lite定位為輕量級無中心化解決方案,使用jar包的形式提供分散式任務的協調服務;Elastic-Job-Cloud採用自研Mesos Framework的解決方案,額外提供資源治理、應用分發以及程式隔離等功能;
- Saturn:是唯品會開源的一個分散式任務排程平臺,在噹噹開源的Elastic Job基礎上,取代傳統的Linux Cron/Spring Batch Job的方式,做到全域統一配置,統一監控,任務高可用以及分片併發處理;
- light-task-scheduler:阿里員工開源的個人專案,主要用於解決分散式任務排程問題,支援實時任務,定時任務和Cron任務。有較好的伸縮性,擴充套件性,健壯穩定性
- Quartz: Java定時任務的標配。利用資料庫的鎖機制實現叢集排程,業務程式碼需要考慮排程的邏輯,對業務程式碼有入侵。
在這之前,我是一個都不知道的..有很多文章對他們進行對比,我們就參考其中一篇(下 2),選擇熱門且成熟的 XXL-JOB 來上手研究一下。
二、XXL-JOB
概述
官方中文文件:http://www.xuxueli.com/xxl-job/
XXL-JOB是一個輕量級分散式任務排程平臺,其核心設計目標是開發迅速、學習簡單、輕量級、易擴充套件。現已開放原始碼並接入多家公司線上產品線,開箱即用。
快速入門 - 本地執行
先定個小目標,先把它在本地跑起來先。
第一步:下載程式碼到本地
找一個合適的目錄,然後執行下列語句把程式碼下載到本地:
$ git clone https://github.com/xuxueli/xxl-job.git複製程式碼
第二步:執行初始化 SQL,再用 IDEA 開啟
找到 /xxl-job/doc/db/table_xxl_job.sql
初始化 SQL 指令碼,並在本地執行。
然後按照 Maven 格式將原始碼匯入 IDEA,原始碼結構如下:
xxl-job-admin:排程中心
xxl-job-core:公共依賴
xxl-job-executor-samples:執行器Sample示例(選擇合適的版本執行器,可直接使用,也可以參考其並將現有專案改造成執行器)
:xxl-job-executor-sample-springboot:Springboot版本,通過Springboot管理執行器,推薦這種方式;
:xxl-job-executor-sample-spring:Spring版本,通過Spring容器管理執行器,比較通用;
:xxl-job-executor-sample-frameless:無框架版本;
:xxl-job-executor-sample-jfinal:JFinal版本,通過JFinal管理執行器;
:xxl-job-executor-sample-nutz:Nutz版本,通過Nutz管理執行器;複製程式碼
第三步:配置並啟動 "排程中心"
排程中心配置檔案地址:
/xxl-job/xxl-job-admin/src/main/resources/xxl-job-admin.properties複製程式碼
排程中心配置內容說明:
### 排程中心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.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
### xxl-job, access token
xxl.job.accessToken=
### xxl-job, i18n (default empty as chinese, "en" as english)
xxl.job.i18n=複製程式碼
在第一次啟動的專案的時候可能會遇到找不到 log 檔案的錯誤(Failed to create
),我們只需要自己手動建立一下就好了,具體可以參照這篇文章:https://blog.csdn.net/leeue/article/details/100779424,記得之後再手動把當前目錄許可權置為可寫狀態哦:sudo chmod 777 xxl-job
當一切配置好了之後,我們就可以啟動專案了,排程中心訪問地址:http://localhost:8080/xxl-job-admin(該地址執行期將會使用到,作為回撥地址),預設登入賬號 "admin/123456",登入後執行介面如下圖所示:
至此,「排程中心」專案已經部署成功了,排程中心叢集(可選)配置可參考官方文件。
第四步:配置啟動"執行器"
執行器配置,配置檔案地址:
/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
### 執行器AppName [選填]:執行器心跳註冊分組依據;為空則關閉自動註冊
xxl.job.executor.appname=xxl-job-executor-sample
### 執行器IP [選填]:預設為空表示自動獲取IP,多網路卡時可手動設定指定IP,該IP不會繫結Host僅作為通訊實用;地址資訊用於 "執行器註冊" 和 "排程中心請求並觸發任務";
xxl.job.executor.ip=
### 執行器埠號 [選填]:小於等於0則自動獲取;預設埠為9999,單機部署多個執行器時,注意要配置不同執行器埠;
xxl.job.executor.port=9999
### 執行器通訊TOKEN [選填]:非空時啟用;(注意與排程中心保持一致)
xxl.job.accessToken=
### 執行器執行日誌檔案儲存磁碟路徑 [選填] :需要對該路徑擁有讀寫許可權;為空則使用預設路徑;
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### 執行器日誌儲存天數 [選填] :值大於3時生效,啟用執行器Log檔案定期清理功能,否則不生效;
xxl.job.executor.logretentiondays=-1複製程式碼
同樣,也要注意一下日誌檔案的建立和許可權問題,解決方法同上。
當配置完成之後執行起來,我們就可以在剛才的任務排程中心的主頁,在右上角的「執行器的數量」上 + 1 了。
第五步:開發第一個任務
當「排程中心」和「執行器」都啟動之後,我們可以直接在「排程中心」的任務管理介面新增一條配置如下圖所示(參考)的任務:
我們點選「操作」按鈕下的「GLUE IDE」可以手動編寫我們要執行的指令碼,我們可以把我們的任務程式碼改寫成如下的樣子:
package com.xxl.job.service.handler;
import com.xxl.job.core.log.XxlJobLogger;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import java.util.concurrent.TimeUnit;
public class DemoGlueJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String param) throws Exception {
XxlJobLogger.log("XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) {
XxlJobLogger.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
return SUCCESS;
}
}複製程式碼
點選「儲存」,然後繼續在「操作」按鈕下點選「執行一次」的操作,就可以在「排程日誌」中看到我們的任務執行情況啦:
可以看到預設執行器中的日誌輸出了:
回頭理解一下過程
到目前為止,我們整個搭建執行的過程都比較順滑,沒有出現什麼阻礙,現在我們稍微來理解一下這個過程。
首先我們在本地初始化了框架提供的 SQL 語句,裡面定義的結構足夠我們不管是單機還是分散式的任務管理需求。然後我們簡單配置了一下連線的資料庫、報警郵件、token 等資訊成功啟動了「排程中心」專案。這個時候專案中預設註冊一個名字為 xxl-job-exectutor-sample
的執行器(名字同預設執行器的 AppName
),並且採用的是自動註冊的方式。
等我們把執行器配置項裡的 xxl,job.admin.addresses
填寫上「排程中心」實際的地址,然後 token 保持與「排程中心」一致,啟動執行器時,執行器就會把自身的一些基礎資訊傳送給「排程中心」,這時候「排程中心」會把接收到的註冊資訊與自身註冊列表裡的 AppName
進行對比(AppName
是每一個執行器的唯一標示),有匹配時就會把 ip
自動填寫上(多個節點就寫多個地址),並在 xxl_job_registry
表上更新資訊。執行器可以簡單理解為專案內嵌了埠為 9999(預設埠)的一個 Server。(架構圖如下)
任務 "執行模式"
在剛才的「快速入門」中,我們新建了一個「GLUE模式(Java)」模式的任務,我們在新建任務時可以直接在「排程中心」上編輯程式碼,然後讓我們的 ”執行器“ 執行,這樣的一種模式是把程式碼直接放在「排程中心」的做法,它的原理是:每個 "GLUE模式(Java)" 任務的程式碼,實際上是“一個繼承自 “IJobHandler” 的實現類的類程式碼”,“執行器”接收到“排程中心”的排程請求時,會通過 Groovy 類載入器載入此程式碼,例項化成 Java 物件,同時注入此程式碼中宣告的 Spring 服務(請確保 Glue 程式碼中的服務和類引用在“執行器”專案中存在),然後呼叫該物件的 execute 方法,執行任務邏輯。
另外一種方式是你提前把程式碼寫進「執行器」程式中,這樣的模式在 XXL-JOB 中叫做「Bean模式」:每個 Bean 模式任務都是一個 Spring 的 Bean 類例項,它被維護在“執行器”專案的 Spring 容器中。任務類需要加 “@JobHandler(value="名稱")” 註解,因為“執行器”會根據該註解識別 Spring 容器中的任務。任務類需要繼承統一介面 “IJobHandler”,任務邏輯在 execute 方法中開發,因為“執行器”在接收到排程中心的排程請求時,將會呼叫 “IJobHandler” 的 execute 方法,執行任務邏輯。
例如在 XXL-JOB 提供的例項程式碼中就有下面這麼一段兒:
package com.xxl.job.executor.service.jobhandler;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHandler;
import com.xxl.job.core.log.XxlJobLogger;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* 任務Handler示例(Bean模式)
*
* 開發步驟:
* 1、繼承"IJobHandler":“com.xxl.job.core.handler.IJobHandler”;
* 2、註冊到Spring容器:新增“@Component”註解,被Spring容器掃描為Bean例項;
* 3、註冊到執行器工廠:新增“@JobHandler(value="自定義jobhandler名稱")”註解,註解value值對應的是排程中心新建任務的JobHandler屬性的值。
* 4、執行日誌:需要通過 "XxlJobLogger.log" 列印執行日誌;
*
* @author xuxueli 2015-12-19 19:43:36
*/
@JobHandler(value="demoJobHandler")
@Component
public class DemoJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String param) throws Exception {
XxlJobLogger.log("XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) {
XxlJobLogger.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
return SUCCESS;
}
}複製程式碼
我們就能在建立任務時直接按照下圖這樣建立,那麼在呼叫任務時,"執行器" 就能夠如願的執行上面的邏輯:
當然 XXL-JOB 還能支援一些指令碼語言型別的模式:
- shell指令碼:任務執行模式選擇為 "GLUE模式(Shell)"時支援 "shell" 指令碼任務;
- python指令碼:任務執行模式選擇為 "GLUE模式(Python)"時支援 "python" 指令碼任務;
- nodejs指令碼:務執行模式選擇為 "GLUE模式(NodeJS)"時支援 "nodejs" 指令碼任務;複製程式碼
三、接入指南
- 前提:已經搭建併成功執行了「排程中心」服務。
快速接入
第一步,我們需要在 pom 檔案中引入 xxl-job-core
的 Maven 依賴,不過比較奇怪的是,明明 Github 上最新版本是 2.1.1,Maven 倉庫上卻沒有最新的包,所以只能用 2.1.0 的:
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.1.0</version>
</dependency>複製程式碼
第二步,在配置檔案中加入 xxl 相關的配置檔案資訊,不管 yml 格式還是 properties 都行,上面提供了 properties 的版本,這了就提供一個 yml 格式的作參考吧:
xxl:
job:
accessToken: xxxx
admin:
addresses: http://127.0.0.1:8080/xxl-job-admin
executor:
appname: test
logpath: /data/applogs/xxl-job/jobhandler
logretentiondays: -1
ip:
port: 9999複製程式碼
第三步,在合適的包目錄下新建 XxlJobConfig
配置類:
@Configuration
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@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(initMethod = "start", destroyMethod = "destroy")
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;
}
/**
* 針對多網路卡、容器內部署等情況,可藉助 "spring-cloud-commons" 提供的 "InetUtils" 元件靈活定製註冊IP;
*
* 1、引入依賴:
* <dependency>
* <groupId>org.springframework.cloud</groupId>
* <artifactId>spring-cloud-commons</artifactId>
* <version>${version}</version>
* </dependency>
*
* 2、配置檔案,或者容器啟動變數
* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
*
* 3、獲取IP
* String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
*/
}複製程式碼
至此,我們的專案就差不多完成了我們的接入工作了,就只剩下開發 Handler 的工作量了。
第四步,建一個示例 DemoJobHandler
在平臺上自測一下:
@JobHandler(value="demoJobHandler")
@Component
public class DemoJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String param) throws Exception {
XxlJobLogger.log("XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) {
XxlJobLogger.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
return SUCCESS;
}
}複製程式碼
然後我們可以啟動專案,看看「排程中心」是否已經成功註冊當前專案的「執行器」,再使用上面介紹的「新建任務」的方法,來測試一下是否正常接入。
小結
總體來說 XXL-JOB 非常的容易上手,並且官方提供了很友好的例項程式碼,包括一些高階特性「分片」、「遠端呼叫」等多種任務都能夠很好的通過示例程式碼理解和使用,這裡就不再詳細贅述了..官方文件已經很完善了,感興趣的小夥伴可以去閱讀以下。
參考資料
- https://www.expectfly.com/2017/08/15/%E5%88%86%E5%B8%83%E5%BC%8F%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1%E6%96%B9%E6%A1%88%E6%8A%80%E6%9C%AF%E9%80%89%E5%9E%8B/ - 分散式定時任務排程系統選型
- https://www.yzhu.name/2019/03/30/Schedule-Job/ - 分散式排程系統選型
- https://blog.csdn.net/qq924862077/article/details/82708610 - XXL-JOB原理--執行器註冊(二)
---
按照慣例黏一個尾巴:
歡迎轉載,轉載請註明出處!
獨立域名部落格:wmyskxz.com
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關注公眾微訊號:wmyskxz
分享自己的學習 & 學習資料 & 生活
想要交流的朋友也可以加qq群:3382693