??定時任務(wù)幾乎是每個業(yè)務(wù)系統(tǒng)必不可少的功能徊哑,計算到期時間袜刷、過期時間等,定時觸發(fā)某項任務(wù)操作莺丑。在使用單體應(yīng)用時著蟹,基本使用Spring提供的注解即可實現(xiàn)定時任務(wù),而在使用微服務(wù)集群時梢莽,這種方式就要考慮添加分布式鎖來防止多個微服務(wù)同時運行定時任務(wù)而導致同一個任務(wù)重復執(zhí)行萧豆。
??除了使用注解,現(xiàn)在還有一種方式昏名,就是搭建分布式任務(wù)平臺涮雷,所有的微服務(wù)注冊到分布式任務(wù)平臺,由分布式任務(wù)平臺統(tǒng)一調(diào)度轻局,這樣避免了同一任務(wù)被重復執(zhí)行洪鸭。這里我們選擇使用XXL-JOB作為分布式任務(wù)調(diào)度平臺样刷,XXL-JOB核心設(shè)計目標是開發(fā)迅速、學習簡單览爵、輕量級置鼻、易擴展。
??使用分布式任務(wù)調(diào)度平臺的優(yōu)點除了避免同一任務(wù)重復執(zhí)行外蜓竹,還有使用簡單箕母,可以手動執(zhí)行、有詳細的調(diào)度日志查看任務(wù)具體執(zhí)行情況等優(yōu)點俱济。
??XXL-JOB官方架構(gòu)設(shè)計圖:
??下面我們按照步驟來介紹嘶是,如何結(jié)合我們的微服務(wù)平臺將分布式任務(wù)調(diào)度平臺XXL-JOB集成進來,實現(xiàn)我們需要的定時任務(wù)功能姨蝴。
一俊啼、微服務(wù)框架整合xxl-job-admin
1、XXL-JOB開源網(wǎng)站下載源碼左医,下載地址 https://github.com/xuxueli/xxl-job/releases ,下載下來的源碼如下:
xxl-job-admin:調(diào)度中心
xxl-job-core:公共依賴
xxl-job-executor-samples:執(zhí)行器Sample示例(選擇合適的版本執(zhí)行器授帕,可直接使用,也可以參考其并將現(xiàn)有項目改造成執(zhí)行器)
:xxl-job-executor-sample-springboot:Springboot版本浮梢,通過Springboot管理執(zhí)行器跛十,推薦這種方式;
:xxl-job-executor-sample-frameless:無框架版本秕硝;
??下載下來的開源包有三個目錄:xxl-job-admin芥映、xxl-job-core和xxl-job-executor-samples,顧名思義远豺,xxl-job-admin是分布式任務(wù)平臺的服務(wù)端兼管理臺奈偏,我們需要部署的也是這個工程,我們可以把整個工程集成到我們的微服務(wù)中躯护,統(tǒng)一打包部署惊来;xxl-job-core是公共依賴包,我們其他需要實現(xiàn)定時任務(wù)的微服務(wù)需要引入這個包來實現(xiàn)定時任務(wù)執(zhí)行器棺滞。xxl-job-executor-samples為定時任務(wù)執(zhí)行器的實例代碼裁蚁。
2、在基礎(chǔ)平臺gitegg-platform工程gitegg-platform-bom中引入xxl-job-core核心包继准,統(tǒng)一版本管理枉证。
......
<!--分布式任務(wù)調(diào)度平臺XXL-JOB核心包-->
<xxl-job.version>2.3.1</xxl-job.version>
......
<!--分布式任務(wù)調(diào)度平臺XXL-JOB核心包-->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${xxl-job.version}</version>
</dependency>
3移必、將xxl-job-admin集成到微服務(wù)工程中勾缭,方便統(tǒng)一打包部署
??根據(jù)我們的微服務(wù)架構(gòu)設(shè)計逸尖,gitegg-plugin作為我們系統(tǒng)的插件工程琉历,里面放置我們需要的插件服務(wù)。有些插件是必須的眨补,而有些插件可能會用不到管削,此時我們就可以根據(jù)自己的業(yè)務(wù)需求去選擇部署業(yè)務(wù)插件倒脓。
??為和我們的微服務(wù)深度集成就不是解耦的特性,我們需要對xxl-job-admin的配置文件進行適當?shù)男薷模?/p>
- 首先修改pom.xml含思,保持各依賴庫版本一致崎弃,修改parent標簽,使其引用GitEgg工程的基礎(chǔ)jar包和微服務(wù)配置注冊功能含潘,同時排除logback饲做,使用log4j2記錄日志
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>gitegg-plugin</artifactId>
<groupId>com.gitegg.cloud</groupId>
<version>1.0.1.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gitegg-job</artifactId>
<name>${project.artifactId}</name>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.test.skip>true</maven.test.skip>
<netty-all.version>4.1.63.Final</netty-all.version>
<gson.version>2.9.0</gson.version>
<spring.version>5.3.20</spring.version>
<spring-boot.version>2.6.7</spring-boot.version>
<mybatis-spring-boot-starter.version>2.2.2</mybatis-spring-boot-starter.version>
<mysql-connector-java.version>8.0.29</mysql-connector-java.version>
<slf4j-api.version>1.7.36</slf4j-api.version>
<junit-jupiter.version>5.8.2</junit-jupiter.version>
<javax.annotation-api.version>1.3.2</javax.annotation-api.version>
<groovy.version>3.0.10</groovy.version>
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.4.0</maven-javadoc-plugin.version>
<maven-gpg-plugin.version>3.0.1</maven-gpg-plugin.version>
</properties>
<dependencies>
<!-- gitegg Spring Boot自定義及擴展 -->
<dependency>
<groupId>com.gitegg.platform</groupId>
<artifactId>gitegg-platform-boot</artifactId>
</dependency>
<!-- gitegg Spring Cloud自定義及擴展 -->
<dependency>
<groupId>com.gitegg.platform</groupId>
<artifactId>gitegg-platform-cloud</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot-starter.version}</version>
<!-- 去除springboot默認的logback配置-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<!-- 去除springboot默認的logback配置-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- freemarker-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
<!-- 去除springboot默認的logback配置-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- mail-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<!-- 去除springboot默認的logback配置-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- starter-actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<!-- 去除springboot默認的logback配置-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
<!--分布式任務(wù)調(diào)度平臺XXL-JOB核心包-->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<!-- 去除沖突的slf4j配置-->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 修改application.properties ,根據(jù)我們系統(tǒng)的規(guī)范遏弱,新增bootstrap.yml盆均、bootstrap-dev.yml、bootstrap-prod.yml漱逸、bootstrap-test.yml文件泪姨。將application.properties部分配置,移到bootstrap.yml配置中饰抒。因xxl-job-admin單獨數(shù)據(jù)庫肮砾,且其默認使用的是Hikari數(shù)據(jù)庫連接池,這里我們不打算改動袋坑,仍然使其保持原有的數(shù)據(jù)庫配置仗处,我們將可配置的內(nèi)容放置在Nacos微服務(wù)配置中心上,同時在bootstrap.yml中添加多yaml文件配置(請注意枣宫,在我們本地使用的是yml結(jié)尾的文件婆誓,Nacos服務(wù)注冊中心上使用的是yaml結(jié)尾的文件,兩者是一樣的也颤,只是擴展名的不同)洋幻。
bootstrap.yml配置:
server:
port: 8007
spring:
profiles:
active: '@spring.profiles.active@'
application:
name: '@artifactId@'
cloud:
inetutils:
ignored-interfaces: docker0
nacos:
discovery:
server-addr: ${spring.nacos.addr}
config:
server-addr: ${spring.nacos.addr}
file-extension: yaml
extension-configs:
# 必須帶文件擴展名,此時 file-extension 的配置對自定義擴展配置的 Data Id 文件擴展名沒有影響
- data-id: ${spring.nacos.config.prefix}.yaml
group: ${spring.nacos.config.group}
refresh: true
- data-id: ${spring.nacos.config.prefix}-xxl-job.yaml
group: ${spring.nacos.config.group}
refresh: true
### xxl-job-admin config
mvc:
servlet:
load-on-startup: 0
static-path-pattern: /static/**
resources:
static-locations: classpath:/static/
### freemarker
freemarker:
templateLoaderPath: classpath:/templates/
suffix: .ftl
charset: UTF-8
request-context-attribute: request
settings.number_format: 0.##########
### actuator
management:
server:
servlet:
context-path: /actuator
health:
mail:
enabled: false
### mybatis
mybatis:
mapper-locations: classpath:/mybatis-mapper/*Mapper.xml
Nacos上gitegg-cloud-config-xxl-job.yaml配置:
server:
servlet:
context-path: /xxl-job-admin
spring:
datasource:
url: jdbc:mysql://127.0.0.1/xxl_job?useSSL=false&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
### datasource-pool
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimum-idle: 10
maximum-pool-size: 30
auto-commit: true
idle-timeout: 30000
pool-name: HikariCP
max-lifetime: 900000
connection-timeout: 10000
connection-test-query: SELECT 1
validation-timeout: 1000
### email
mail:
host: smtp.qq.com
port: 25
username: xxx@qq.com
from: xxx@qq.com
password: xxx
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
socketFactory:
class: javax.net.ssl.SSLSocketFactory
### xxl-job, access token
xxl:
job:
accessToken: default_token
### xxl-job, i18n (default is zh_CN, and you can choose "zh_CN", "zh_TC" and "en")
i18n: zh_CN
## xxl-job, triggerpool max size
triggerpool:
fast:
max: 200
slow:
max: 100
### xxl-job, log retention days
logretentiondays: 30
4歇拆、初始化xxl-job-admin需要的數(shù)據(jù)庫腳本
??初始化腳本存放在下載的包目錄的\xxl-job-2.3.1\doc\db\tables_xxl_job.sql中鞋屈,一共需要8張表。我們將xxl-job-admin的數(shù)據(jù)庫和業(yè)務(wù)數(shù)據(jù)庫分開故觅,配置不同的數(shù)據(jù)源厂庇,在Nacos配置單獨的xxl-job-admin配置文件。
- 新建xxl_job數(shù)據(jù)庫
- 打開數(shù)據(jù)庫執(zhí)行建表語句
5输吏、在GitEgg工程的父級pom.xml下添加靜態(tài)文件過濾
??xxl-job-admin是SpringMVC項目权旷,其前端頁面由ftl文件和靜態(tài)文件組成,默認情況下maven啟用分環(huán)境讀取配置時,會對resource目錄下的@進行替換拄氯,導致靜態(tài)文件下的字體文件不能用躲查,所以,這里需要進行和jks文件一樣的過濾配置:
<resources>
<!-- 增加分環(huán)境讀取配置 -->
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/*.jks</exclude>
<exclude>static/**</exclude>
</excludes>
</resource>
<!-- 解決jks被過濾掉的問題 -->
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>**/*.jks</include>
<include>static/**</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
6译柏、在Gateway添加xxl-job-admin路由轉(zhuǎn)發(fā)
??xxl-job-admin路由轉(zhuǎn)發(fā)需要添加兩方面內(nèi)容镣煮,一個是xxl-job-admin注冊到Nacos注冊中心上的gitegg-job服務(wù),一個是xxl-job-admin前端頁面請求的靜態(tài)文件轉(zhuǎn)發(fā)鄙麦。第一個是為了和我們整體微服務(wù)保持一致典唇,第二個是為了解決xxl-job-admin前端ftl頁面在請求靜態(tài)文件時,請求的是/xxl-job-admin根路徑胯府。新增Gateway路由轉(zhuǎn)發(fā)配置如下:
- id: gitegg-job
uri: lb://gitegg-job
predicates:
- Path=/gitegg-job/**
filters:
- StripPrefix=1
- id: xxl-job-admin
uri: lb://gitegg-job
predicates:
- Path=/xxl-job-admin/**
filters:
- StripPrefix=0
7介衔、增加xxl-job-admin訪問白名單
??xxl-job-admin有自己的權(quán)限訪問控制,我們不在網(wǎng)關(guān)對其進行鑒權(quán)骂因,所以在Nacos配置中炎咖,增加白名單配置:
# 網(wǎng)關(guān)放行設(shè)置 1、whiteUrls不需要鑒權(quán)的公共url寒波,白名單乘盼,配置白名單路徑 2、authUrls需要鑒權(quán)的公共url
oauth-list:
......
whiteUrls:
......
- "/gitegg-job/**"
- "/xxl-job-admin/**"
......
8影所、啟動xxl-job-admin微服務(wù)蹦肴,查看是否啟動成功,默認用戶名密碼: admin/123456
二猴娩、測試XXL-JOB定時任務(wù)功能
??我們在上面的第一步中阴幌,完成了xxl-job-admin的整合和啟動,xxl-job-admin可以看做是分布式任務(wù)的服務(wù)注冊中心和管理臺卷中,如果我們需要實現(xiàn)定時任務(wù)矛双,還需要具體實現(xiàn)執(zhí)行器讓xxl-job-admin調(diào)用執(zhí)行。
??XXL-JOB支持多種方式的定時任務(wù)調(diào)用蟆豫,可以將定時任務(wù)執(zhí)行器寫在業(yè)務(wù)代碼中议忽,也可以寫在xxl-job-admin服務(wù)端:
- BEAN模式(類形式): Bean模式任務(wù),支持基于類的開發(fā)方式十减,每個任務(wù)對應(yīng)一個Java類栈幸。
- BEAN模式(方法形式): Bean模式任務(wù),支持基于方法的開發(fā)方式帮辟,每個任務(wù)對應(yīng)一個方法速址。
- GLUE模式(Java/Shell/Python/NodeJS/PHP/PowerShell) :任務(wù)以源碼方式維護在調(diào)度中心,支持通過Web IDE在線更新由驹,實時編譯和生效芍锚,因此不需要指定JobHandler。
1、增加xxl-job通用配置
??新增gitegg-platform-xxl-job工程并炮,增加通用配置XxlJobConfig.java通用配置默刚,這樣在需要使用定時任務(wù)的微服務(wù)中,只需要引入一次即可逃魄,不需要重復配置荤西。
XxlJobConfig.java:
package com.gitegg.platform.xxl.job.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* xxl-job config
*
* @author xuxueli 2017-04-28
*/
@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(">>>>>>>>>>> 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;
}
/**
* 針對多網(wǎng)卡、容器內(nèi)部署等情況嗅钻,可借助 "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();
*/
}
Nacos配置中心:
xxl:
job:
admin:
addresses: http://127.0.0.1/xxl-job-admin
accessToken: 'default_token'
executor:
appname: ${spring.application.name}
address:
ip:
port: 9999
logpath: D:\\log4j2_nacos\\xxl-job\\jobhandler
logretentiondays: 30
2柳弄、實現(xiàn)定時任務(wù)測試代碼
??我們在gitegg-service-system中測試定時任務(wù)執(zhí)行器,先在pom.xml中添加gitegg-platform-xxl-job依賴概说,然后新增SystemJobHandler.java測試類
SystemJobHandler.java:
package com.gitegg.service.system.jobhandler;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* 定時任務(wù)示例代碼碧注,其他更多示例請查看
* https://www.xuxueli.com/xxl-job
* @author GitEgg
*/
@Slf4j
@Component
public class SystemJobHandler {
/**
* 1、簡單任務(wù)示例(Bean模式)不帶返回值
*/
@XxlJob("systemJobHandler")
public void systemJobHandler() throws Exception {
XxlJobHelper.log("不帶返回值:XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) {
XxlJobHelper.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
}
/**
* 2糖赔、簡單任務(wù)示例(Bean模式)帶成功或失敗返回值
*/
@XxlJob("userJobHandler")
public ReturnT<String> userJobHandler() throws Exception {
XxlJobHelper.log("帶返回值:XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) {
XxlJobHelper.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
return ReturnT.SUCCESS;
}
}
3萍丐、配置xxl-job-admin新增執(zhí)行器
-
新增時:
-
gitegg-service-system服務(wù)啟動后,自動注冊:
4放典、新增xxl-job-admin任務(wù)
??執(zhí)行器可以看做是一組微服務(wù)逝变,而任務(wù)是微服務(wù)具體執(zhí)行的方法。任務(wù)新增后奋构,默認是STOP狀態(tài)壳影,需要手動啟動,當列表顯示RUNNING時弥臼,表示該任務(wù)是運行狀態(tài)宴咧,會根據(jù)配置的時間執(zhí)行。
5径缅、查看執(zhí)行器是否執(zhí)行
??在本地開發(fā)環(huán)境查看任務(wù)執(zhí)行的方式有多種掺栅,直接Debug也可以,生產(chǎn)環(huán)境我們可以查看xxl-job日志纳猪,在測試代碼中記錄的log氧卧,在xxl-job-admin管理臺都可以詳細查看。
??通過以上操作步驟兆旬,我們將xxl-job和xxl-job-admin整合到了我們的微服務(wù)架構(gòu)中假抄,只需要在有任務(wù)調(diào)度需求的微服務(wù)中實現(xiàn)執(zhí)行器就可以滿足我們的需求了。
GitEgg-Cloud是一款基于SpringCloud整合搭建的企業(yè)級微服務(wù)應(yīng)用開發(fā)框架,開源項目地址:
Gitee: https://gitee.com/wmz1930/GitEgg
GitHub: https://github.com/wmz1930/GitEgg
歡迎感興趣的小伙伴Star支持一下宿饱。