天天看點

分布式定時排程:xxl-job 萬字詳解

作者:儒雅程式員阿鑫
上周的時候,一位粉絲朋友在做資料采集,因為自己實作的方式性能比較一般,和我在交流如何優化,由于細節比較的多,也就花了三個晚上的時間電話交流,把業務從頭到尾幫忙梳理了一遍,并給出了完整的優化方案;其中有部分定時執行的任務,也就就聊到了常用的定時任務架構

今天給大家分享一篇XXL-JOB定時任務排程架構的使用;

一.定時任務概述

1.定時任務認識

1.1.什麼是定時任務

定時任務是按照指定時間周期運作任務。使用場景為在某個固定時間點執行,或者周期性的去執行某個任務,比如:每天晚上24點做資料彙總,定時發送短信等。

1.2.常見定時任務方案

  • While + Sleep : 通過循環加休眠的方式定時執行
  • Timer和TimerTask實作 :JDK自帶的定時任務,可以實作簡單的間隔執行任務(在指定時間點執行某一任務,也能定時的周期性執行),無法實作按月曆去排程執行任務。
  • ScheduledExecutorService : Java并發包下,JDK1.5出現,是比較理想的定時任務實作方案。Eureka就使用的是它
  • QuartZ : 使用Quartz,它是一個異步任務排程架構,功能豐富,可以實作按月曆排程,支援持久化。
  • 使用Spring Task,Spring 3.0後提供Spring Task實作任務排程,支援按月曆排程,相比Quartz功能稍簡單,但是在開發基本夠用,支援注解程式設計方式。
  • SpringBoot中的Schedule : 通過@EnableScheduling+@Scheduled最實作定時任務,底層使用的是Spring Task

2.分布式定時任務

2.1.遇到什麼問題

上述的定時任務都是集中式(單體項目使用)的定時任務,在分布式中将會面臨一些問題或不足

  • 業務量大,單機性能瓶頸需要擴充
  • 多台機器部署如何保證定時任務不重複執行
  • 定時任務時間需要可調整,可以暫停
  • 機器發生故障down機,定時任務依然可用,如何實作故障轉移
  • 定時任務,執行日志是否可監控

2.2.分布式定時任務xxl-job

XXL-JOB是一個分布式任務排程平台,于2015問世,其核心設計目标是開發迅速、學習簡單、輕量級、易擴充。現已開放源代碼并接入多家公司線上産品線,開箱即用。其具備且不止如下能力

  1. 簡單:支援通過Web頁面對任務進行CRUD操作,操作簡單,一分鐘上手;
  2. 動态:支援動态修改任務狀态、啟動/停止任務,以及終止運作中任務,即時生效;
  3. 排程中心HA(中心式):排程采用中心式設計,“排程中心”基于叢集Quartz實作并支援叢集部署,可保證排程中心HA;執行器HA(分布式):任務分布式執行,任務"執行器"支援叢集部署,可保證任務執行HA;
  4. 彈性擴容縮容:一旦有新執行器機器上線或者下線,下次排程時将會重新配置設定任務;
  5. 路由政策:執行器叢集部署時提供豐富的路由政策,包括:第一個、最後一個、輪詢、随機、一緻性HASH、最不經常使用、最近最久未使用、故障轉移、忙碌轉移等;
  6. 故障轉移:任務路由政策選擇"故障轉移"情況下,如果執行器叢集中某一台機器故障,将會自動Failover切換到一台正常的執行器發送排程請求。
  7. 任務失敗告警:預設提供郵件方式失敗告警,同時預留擴充接口,可方面的擴充短信、釘釘等告警方式;
具體見:https://github.com/xuxueli/xxl-job/tree/v2.0.0

二.XXL-JOB初體驗

1.xxl-job架構設計

1.1.設計思想

将排程行為抽象形成“排程中心”公共平台,而平台自身并不承擔業務邏輯,“排程中心”負責發起排程請求。

将任務抽象成分散的JobHandler,交由“執行器”統一管理,“執行器”負責接收排程請求并執行對應的JobHandler中業務邏輯。是以,“排程”和“任務”兩部分可以互相解耦,提高系統整體穩定性和擴充性;

1.2.架構設計圖

xxl-job分為 排程中心和執行器兩大子產品

  • 排程子產品(排程中心)

負責管理排程資訊,按照排程配置發出排程請求,自身不承擔業務代碼。排程系統與任務解耦,提高了系統可用性和穩定性,同時排程系統性能不再受限于任務子產品;

支援可視化、簡單且動态的管理排程資訊,包括任務建立,更新,删除,GLUE開發和任務報警等,所有上述操作都會實時生效,同時支援監控排程結果以及執行日志,支援執行器Failover(故障轉移)。

  • 執行子產品(執行器)

負責接收排程請求并執行任務邏輯。任務子產品專注于任務的執行等操作,開發和維護更加簡單和高效;

接收“排程中心”的執行請求、終止請求和日志請求等。

分布式定時排程:xxl-job 萬字詳解
  • 排程中心高可用

基于資料庫的叢集方案,資料庫選用Mysql;叢集分布式并發環境中進行定時任務排程時,會在各個節點會上報任務,存到資料庫中,執行時會從資料庫中取出觸發器來執行,如果觸發器的名稱和執行時間相同,則隻有一個節點去執行此任務。

  • 并行排程

排程采用線程池方式實作,避免單線程因阻塞而引起任務排程延遲。XXL-JOB排程子產品預設采用并行機制,在多線程排程的情況下,排程子產品被阻塞的幾率很低,大大提高了排程系統的承載量。

XXL-JOB的不同任務之間并行排程、并行執行。XXL-JOB的單個任務,針對多個執行器是并行運作的,針對單個執行器是串行執行的。同時支援任務終止。

  • 執行器(任務)高可用

執行器如若叢集部署,排程中心将會感覺到線上的所有執行器,如“127.0.0.1:9997, 127.0.0.1:9998, 127.0.0.1:9999”。多個執行器可以選擇“路由政策”來采用輪詢,随機等方式進行多機器排程。

當任務”路由政策”選擇”故障轉移(FAILOVER)”時,當排程中心每次發起排程請求時,會按照順序對執行器發出心跳檢測請求,第一個檢測為存活狀态的執行器将會被標明并發送排程請求。排程成功後,可在日志監控界面檢視“排程備注”

2.xxl-job安裝

2.1.下載下傳源碼

請下載下傳項目源碼并解壓,使用IDEA工具導入項目

源碼倉庫位址

  • https://github.com/xuxueli/xxl-job
  • http://gitee.com/xuxueli0323/xxl-job

項目代碼結構如下

分布式定時排程:xxl-job 萬字詳解
  • doc :文檔,即SQL腳本所在目錄
  • db : “排程資料庫”建表腳本
  • xxl-job-admin : 排程中心項目源碼
  • xxl-job-core : 核心子產品,公共Jar依賴
  • xxl-job-executor-samples : 執行器,Sample示例項目(大家可以在該項目上進行開發,也可以将現有項目改造生成執行器項目)

2.2.導入資料庫

打開項目代碼,擷取 “排程資料庫初始化SQL腳本” 并執行即可。“排程資料庫初始化SQL腳本” 位置為: /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:系統使用者表;

2.3.啟動排程中心

打開 xxl-job-admin 的配置檔案,/xxl-job/xxl-job-admin/src/main/resources/application.properties 對排程中心進行配置,重要配置如下

  • server.port : 根據情況修改端口
  • spring.datasource.url :指向剛才準備的資料庫
  • spring.datasource.password : 記得修改成自己的資料庫密碼
  • spring.mail.username :配置自己的郵件賬号
  • spring.mail.password :郵件的授權碼,我下面是以qq郵箱為例

下面根據自己的情況進行修改,不要直接複制

### 排程中心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
[email protected]
spring.mail.password=郵箱授權碼,不是登入密碼
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
           

然後啟動排程中心 ,執行 XxlJobAdminApplication#main 方法 , 啟動之後,浏覽器通路 http://localhost:18080/xxl-job-admin/jobinfo?jobGroup=2 ;注意URL中有個上下文路徑。預設登入賬号 “admin/123456”, 登入後運作界面如下圖所示。

分布式定時排程:xxl-job 萬字詳解

2.3.配置部署“執行器項目

“執行器”項目:xxl-job-executor-sample-springboot (提供多種版本執行器供選擇,現以 springboot 版本為例,可直接使用,也可以參考其并将現有項目改造成執行器)

作用:負責接收“排程中心”的排程并執行;可直接部署執行器,也可以将執行器內建到現有業務項目中。

修改配置:/xxl-job/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/application.properties

  • xxl.job.admin.addresses : 排程中心的位址,如果排程中心修改過端口,這裡也要對應修改
### 排程中心部署跟位址 [選填]:如排程中心叢集部署存在多個位址則用逗号分隔。執行器将會使用該位址進行"執行器心跳注冊"和"任務結果回調";為空則關閉自動注冊;
xxl.job.admin.addresses=http://127.0.0.1:18080/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
           

上面配置是為了在Spring容器中建立一個 XxlJobSpringExecutor 執行器Bean,見:com.xxl.job.executor.core.config.XxlJobConfig#xxlJobExecutor

@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);

    return xxlJobSpringExecutor;
}
           

在com.xxl.job.executor.service.jobhandler.SampleXxlJob中提供了簡單的定時任務執行個體

為友善使用者參考與快速實用,示例執行器内原生提供多個Bean模式任務Handler,可以直接配置實用,如下:

  • demoJobHandler:簡單示例任務,任務内部模拟耗時任務邏輯,使用者可線上體驗Rolling Log等功能;
  • shardingJobHandler:分片示例任務,任務内部模拟處理分片參數,可參考熟悉分片任務;
  • httpJobHandler:通用HTTP任務Handler;業務方隻需要提供HTTP連結等資訊即可,不限制語言、平台。示例任務入參如下:
/**
 * XxlJob開發示例(Bean模式)
 *
 * 開發步驟:
 * 1、在Spring Bean執行個體中,開發Job方法,方式格式要求為 "public ReturnT<String> execute(String param)"
 * 2、為Job方法添加注解 "@XxlJob(value="自定義jobhandler名稱", init = "JobHandler初始化方法", destroy = "JobHandler銷毀方法")",注解value值對應的是排程中心建立任務的JobHandler屬性的值。
 * 3、執行日志:需要通過 "XxlJobLogger.log" 列印執行日志;
 *
 * @author xuxueli 2019-12-11 21:52:51
 */
@Component
public class SampleXxlJob {
    private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class);
 /**
     * 1、簡單任務示例(Bean模式)
     */
    @XxlJob("demoJobHandler")
    public ReturnT<String> demoJobHandler(String param) throws Exception {
        logger.info("XXL-JOB, Hello World. param={}",param);
        return ReturnT.SUCCESS;
    }
    //...省略...
}
           
【重要】 如果我們要寫自己的定時任務,參照上面方法,在方法上注解一個@XxlJob("任務名字") ,方法可以接受一個字元串參數,方法需要傳回ReturnT格式。

最後啟動執行器項目.

3.配置定時任務

3.1.執行器建立

打開排程中心可視化界面,在執行器管理界面,添加新增執行器

分布式定時排程:xxl-job 萬字詳解
  • appName : 執行器的名字,可以任意填寫
  • 名稱:任意填寫
  • 注冊方式:排程中心是通過RPC的方式對執行器發起排程,是以這裡需要的是執行器項目的ip:port ,注意,該端口不是執行器項目的server.port ,而是:xxl.job.executor.port 端口。你可以選擇自動注冊,也可以手動錄入。

3.2.建立任務

在 任務管理 界面,新增任務

分布式定時排程:xxl-job 萬字詳解
  • 路由政策:有輪詢,随機,故障轉移等等政策,是用在叢集模式下的排程方式。
  • cron : 定時任務的執行時間規則,時間表達式
  • JobHandler : 這個是要對應 “執行器項目”中 @XxlJob("demoJobHandler") 注解中的名字
  • 運作模式 :Bean ,使用内置代碼方式,也可以執行線上執行代碼方式
  • 報警郵件 :如果定時任務失敗,會發送報警郵件到郵箱
  • 任務參數:這個參數可以傳遞給 @XxlJob("demoJobHandler")所在方法的參數。

建立好任務之後就可以執行了

分布式定時排程:xxl-job 萬字詳解

排程日志

分布式定時排程:xxl-job 萬字詳解

IDEA工具控制台效果

15:47:33.017 logback [Thread-16] INFO  c.x.j.e.s.jobhandler.SampleXxlJob - XXL-JOB, Hello World. param=
15:47:34.007 logback [Thread-16] INFO  c.x.j.e.s.jobhandler.SampleXxlJob - XXL-JOB, Hello World. param=
15:47:35.008 logback [Thread-16] INFO  c.x.j.e.s.jobhandler.SampleXxlJob - XXL-JOB, Hello World. param=
...省略...
           

4.GLUE模式(Java)

4.1.添加任務

該模式支援線上編輯定時任務的内容,立刻執行,無需再開發工具中編輯代碼,也無需重新開機項目。

請點選任務右側 “GLUE” 按鈕,進入 “GLUE編輯器開發界面” ,見下圖。“GLUE模式(Java)” 運作模式的任務預設已經初始化了示例任務代碼,即列印Hello World。

分布式定時排程:xxl-job 萬字詳解

任務以源碼方式維護在排程中心,支援通過Web IDE線上更新,實時編譯和生效,是以不需要指定JobHandler

4.2.編寫代碼

儲存之後可以在操作按鈕裡面去編寫任務

分布式定時排程:xxl-job 萬字詳解

(“GLUE模式(Java)” 運作模式的任務實際上是一段繼承自IJobHandler的Java類代碼,它在執行器項目中運作,可使用@Resource/@Autowire注入執行器裡中的其他服務),比如我的定時任務如下,編輯好之後點選儲存

分布式定時排程:xxl-job 萬字詳解

儲存好之後,啟動定時任務,效果如下

分布式定時排程:xxl-job 萬字詳解

三.XXL-JOB叢集部署

1.排程中心叢集

1.1.問題概述

排程中心支援叢集部署,提升排程系統容災和可用性。排程中心叢集部署時,幾點要求和建議:

  • DB配置保持一緻;
  • 叢集機器時鐘保持一緻(單機叢集忽視);
  • 當啟動多個排程器時,執行器配置排程中心部署跟位址可以用逗号分隔。執行器将會使用該位址進行"執行器心跳注冊"和"任務結果回調";為空則關閉自動注冊;
但是建議:推薦通過nginx為排程中心叢集做負載均衡,配置設定域名。排程中心通路、執行器回調配置、調用API服務等操作均通過該域名進行。

1.2.啟動多個排程中心

修改排程中心端口,啟動多個排程中心,我這裡啟動兩個如

  • http://localhost:18080/xxl-job-admin/
  • http://localhost:18081/xxl-job-admin/

1.3.配置Nginx負載均衡

當啟動多個排程器時,執行器配置排程中心部署跟位址可以用逗号分隔。執行器将會使用該位址進行“執行器心跳注冊”和“任務結果回調”;為空則關閉自動注冊;

但是建議:推薦通過nginx為排程中心叢集做負載均衡,配置設定域名。排程中心通路、執行器回調配置、調用API服務等操作均通過該域名進行。

我們啟動了2個排程中心,那麼我的執行器項目該注冊到哪個排程中心呢?我們通過Nginx來解決這個問題,原理如下圖:

分布式定時排程:xxl-job 萬字詳解

我們再hosts配置 www.jobs.com作為nginx的主機域名,然後反向代理到多個排程中心,這樣一來執行器就隻需要注冊到www.jobs.com Nginx即可。

修改 C:\Windows\System32\drivers\etc\hosts增加配置如下

127.0.0.1 www.jobs.com           

Nginx配置如下

#排程中心
upstream jobs{
 server localhost:18080;
 server localhost:18081;
}

server {
      listen       80;
      #使用域名
      server_name  www.jobs.com;

      #charset koi8-r;

      #access_log  logs/host.access.log  main;

      location / {
       #排程中心反向代理配置
          proxy_pass   http://jobs/;
      }

      #error_page  404              /404.html;

      # redirect server error pages to the static page /50x.html
      #
      error_page   500 502 503 504  /50x.html;
      location = /50x.html {
          root   html;
      }

      
  }
           

啟動Nginx,通過浏覽器通路 http://www.jobs.com/xxl-job-admin/ ,可以通路到排程中心的管理界面

2.執行器項目叢集

執行器支援叢集部署,提升排程系統可用性,同時提升任務處理能力。

執行器叢集部署時,幾點要求和建議:

  • 執行器回調位址(xxl.job.admin.addresses)需要保持一緻;執行器根據該配置進行執行器自動注冊等操作。
  • 同一個執行器叢集内AppName(xxl.job.executor.appname)需要保持一緻;排程中心根據該配置動态發現不同叢集的線上執行器清單。

2.1.啟動多個執行器項目

現在對執行器項目做叢集,修改xxl-job-executor-sample-springboot配置檔案application.properties

  • server.port : 既然是做叢集,項目端口需要修改
  • xxl.job.admin.addresses : 排程中心位址需要修改成www.jobs.com ,多個執行器配置同一個位址。
  • xxl.job.executor.port : RPC通信端口也要修改,多個執行器該端口需要不一樣

第一個執行個體配置

server.port=19090
xxl.job.admin.addresses=http://www.jobs.com/xxl-job-admin #對應Nginx位址
xxl.job.executor.port=9999
           

第二個執行個體配置

server.port=19091
xxl.job.admin.addresses=http://www.jobs.com/xxl-job-admin
xxl.job.executor.port=9998
           

在 Configurations中配置,允許啟動多個執行個體

分布式定時排程:xxl-job 萬字詳解

啟動執行個體如下

分布式定時排程:xxl-job 萬字詳解

2.2.配置定時任務

通過http://www.jobs.com/xxl-job-admin 通路排程中心管理界面,在執行器管理中可以看到多台執行器執行個體

分布式定時排程:xxl-job 萬字詳解

在任務管理中,可以編輯任務,然後選擇路由政策,比如:選擇輪詢,然後啟動任務,就會看到兩個執行器項目輪着執行定時任務。

分布式定時排程:xxl-job 萬字詳解

說在最後

xxl-job确實很強大,功能也很全,經過該文章學習相信你可以把xxl-job給用起來了,但是如果你的項目是一個小體量的單體,我不太建議使用它,Quzrtz或者SpringBoot Task就足夠 ,對于xxl-job個人還是有些笨重。

來源:blog.csdn.net/u014494148/article/details/121382211

繼續閱讀