大纲
前言
体系架构
XXL-JOB 框架中包含了两个核心模块:调度中心和执行器,其中调度中心(服务端)主要负责任务的调度,而执行器(客户端)负责任务的执行。v2.1.0
版本的架构图如下:
术语解释
术语 | 描述 |
---|
执行器 | 真正执行调度任务的应用(客户端) |
路由策略 | 路由的选择方式,即调度任务的分配规则,使用分片广播时,需要结合代码使用 |
Cron | 定时任务的触发时间规则 |
运行模式 | - Bean:执行器要执行指定的 Bean 对象的方式 - GLUE (Java):以源码的方式来维护调度中心,相当于就是将调度中心变成一个执行器,每次调用运行指定的脚本 |
JobHandler | 执行器执行的业务逻辑(调度任务) |
子任务 ID | 当前任务执行完之后,下一个所要执行任务的 ID(支持多个子任务) |
任务超时时间 | 当执行任务的时间大于规定时间时,就算任务超时 |
阻塞处理策略 | 任务调度过于密集,执行器来不及处理时的处理策略,一共有三种阻塞处理策略 - 单机串行:按顺序一个一个地执行完任务 - 丢弃后续调度:执行前一个任务,后面的调度任务全部丢弃,直到前一个任务执行完成 - 覆盖之前调度:后一个任务去覆盖前一个任务,前一个任务不再执行
|
代码下载
本文的案例代码都可以在 这里 下载得到,直接作为 Maven 项目导入到 IDEA 或者 Eclipse 即可。
XXL-JOB 介绍
调度中心的核心模块
模块名称 | 说明 |
---|
定时模块 Scheduled | 定时去获取任务调度的数据,数据存储在数据库中 |
路由模块 Route | 一定的路由规则,负责计算出要指定的执行器 |
远程调用 RPC | 通过远程调用执行执行器,将远程调用的信息发给执行器,信息包括哪个执行器,哪个任务 |
提示:执行器(客户端)中也有一个 RPC 用于通信,负责接收任务。
调度中心的执行流程
- 1、启动服务,将执行器注册到调度中心(注意每 30s 重新注册到调度中心)
- 2、在数据库中存储着执行器的数据,也可以手动地去录入执行器
- 3、配置调度中心、定时调度、配置路由规则
- 4、远程调用指定的执行器实例,找到对应的任务进行调用
调度中心的设计思想
- 将调度行为抽象形成 “调度中心” 公共平台,而平台自身并不承担业务逻辑,” 调度中心” 负责发起调度请求。
- 将任务抽象成分散的 JobHandler,交由 “执行器” 统一管理,” 执行器” 负责接收调度请求并执行对应的 JobHandler 中业务逻辑。因此,” 调度” 和 “任务” 两部分可以相互解耦,提高系统整体稳定性和扩展性。
- 调度模块(调度中心):负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块。
- 支持可视化、简单且动态的管理调度信息,包括任务新建、更新、删除、GLUE 开发和任务报警等,所有上述操作都会实时生效,同时支持监控调度结果以及执行日志,支持执行器 Failover。
- 执行模块(执行器):负责接收调度请求并执行任务逻辑,任务模块专注于任务的执行等操作,开发和维护更简单和高效;接收 “调度中心” 的执行请求、终止请求和日志请求等。
提示:调度中心大体的架构设计图可查看 这里。
快速入门案例
创建 POM 模块
为了后续方便管理多个子模块,这里先创建 Maven 的 POM 模块 xxl-job-study
,XML 配置文件的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?xml version="1.0" encoding="UTF-8"?>
<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"> <modelVersion>4.0.0</modelVersion>
<groupId>com.clay</groupId> <artifactId>xxl-job-study</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties>
<modules> <module>xxl-job-executor-spring</module> <module>xxl-job-executor-springboot</module> </modules>
</project>
|
基于 Spring 开发
框架版本说明
框架 | 版本 |
---|
Spring | 5.3.23 |
XXL-JOB | 2.4.0 |
项目目录结构
创建 Maven 子模块
在 Maven 的 POM 模块中创建子模块 xxl-job-executor-spring
,XML 配置文件的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| <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"> <modelVersion>4.0.0</modelVersion>
<parent> <groupId>com.clay</groupId> <artifactId>xxl-job-study</artifactId> <version>1.0-SNAPSHOT</version> </parent>
<artifactId>xxl-job-executor-spring</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version>
<dependencies> <dependency> <groupId>com.xuxueli</groupId> <artifactId>xxl-job-core</artifactId> <version>2.4.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.23</version> </dependency> <dependency> <groupId>org.logback-extensions</groupId> <artifactId>logback-ext-spring</artifactId> <version>0.1.4</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.25</version> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.3.2</version> <configuration> <archiveClasses>false</archiveClasses> </configuration> </plugin> </plugins> </build>
</project>
|
创建项目的配置文件
创建执行器的配置文件
在子模块的 /src/main/resources
目录下创建 xxl-job-executor.properties
配置文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
xxl.job.accessToken=default_token
xxl.job.executor.appname=xxl-job-executor-spring
xxl.job.executor.address=
xxl.job.executor.ip=
xxl.job.executor.port=9999
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
xxl.job.executor.logretentiondays=30
|
参数名称 | 参数说明 |
---|
xxl.job.executor.appname | 执行器的名称,建议使用 Maven 模块的名称 |
xxl.job.executor.address | 执行器的注册地址,使用新版本(如 v2.4.0)时,必须带 HTTP/HTTPS 协议头,例如 http://127.0.0.1:9999 |
xxl.job.accessToken | XXL-JOB 的访问令牌,只有调度中心和执行器双方的 AccessToken 互相匹配才允许通讯 |
特别注意
XXL-JOB 从 v.2.3.1
版本开始,调度通讯默认启用 AccessToken
,且默认的 AccessToken
是 default_token
。
创建 Spring 的配置文件
在子模块的 /src/main/resources
目录下创建 applicationcontext-xxl-job.xml
配置文件,这里主要需要指定 JobHandler 的扫描路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="propertyConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"> <property name="fileEncoding" value="utf-8"/> <property name="locations"> <list> <value>classpath*:xxl-job-executor.properties</value> </list> </property> </bean>
<context:component-scan base-package="com.clay.job.executor"/>
<bean id="xxlJobSpringExecutor" class="com.xxl.job.core.executor.impl.XxlJobSpringExecutor"> <property name="adminAddresses" value="${xxl.job.admin.addresses}"/> <property name="accessToken" value="${xxl.job.accessToken}"/> <property name="appname" value="${xxl.job.executor.appname}"/> <property name="address" value="${xxl.job.executor.address}"/> <property name="ip" value="${xxl.job.executor.ip}"/> <property name="port" value="${xxl.job.executor.port}"/> <property name="logPath" value="${xxl.job.executor.logpath}"/> <property name="logRetentionDays" value="${xxl.job.executor.logretentiondays}"/> </bean>
</beans>
|
创建 Logback 的配置文件
在子模块的 /src/main/resources
目录下创建 logback.xml
配置文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <?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-executor-spring.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>
|
创建 Web 容器的配置文件
在子模块的 /src/main/webapp/WEB-INF
目录下创建 web.xml
配置文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>xxl-job-executor-spring</display-name> <context-param> <param-name>webAppRootKey</param-name> <param-value>xxl-job-executor-spring</param-value> </context-param>
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationcontext-*.xml</param-value> </context-param>
<context-param> <param-name>logbackConfigLocation</param-name> <param-value>classpath:logback.xml</param-value> </context-param>
<listener> <listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class> </listener>
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
<welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app>
|
创建自定义的执行器类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package com.clay.job.executor;
import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component public class CustomJobExecutor {
private static final Logger logger = LoggerFactory.getLogger(CustomJobExecutor.class);
@XxlJob(value = "sampleJobHandler") public void sampleJobHandler() throws Exception { XxlJobHelper.log("XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) { XxlJobHelper.log("beat at:" + i); TimeUnit.SECONDS.sleep(2); }
XxlJobHelper.handleSuccess(); }
}
|
部署应用到 Tomcat 服务器
测试任务调度代码
启动调度中心服务
调度中心添加执行器
登录 XXL-JOB 调度中心的管理页面,添加自定义的执行器信息,这里的 AppName
是在 xxl-job-executor.properties
配置文件中使用 xxl.job.executor.appname
参数指定的。
确定执行器自动注册成功 ,添加执行器后一般需要等待 10 秒左右。
特别注意
XXL-JOB 使用新版本(如 v2.4.0
)时,如果添加执行器选择的是手动录入模式,那么此时填写的机器地址必须是带 HTTP/HTTPS 协议头(例如 http://127.0.0.1:9999
),否则后续添加的调度任务无法正常执行。
调度中心添加调度任务
这里的 JobHandler
配置内容,是在自定义的执行器类里使用 @XxlJob
注解的 value
属性指定。
调度中心启动调度任务
查看任务调度日志信息
基于 SpringBoot 开发
框架版本说明
框架 | 版本 |
---|
Spring Boot | 2.7.9 |
XXL-JOB | 2.4.0 |
项目目录结构
创建 Maven 子模块
在 Maven 的 POM 模块中创建子模块 xxl-job-executor-springboot
,XML 配置文件的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| <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"> <modelVersion>4.0.0</modelVersion>
<parent> <groupId>com.clay</groupId> <artifactId>xxl-job-study</artifactId> <version>1.0-SNAPSHOT</version> </parent>
<artifactId>xxl-job-executor-springboot</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version>
<properties> <xxl-job.version>2.4.0</xxl-job.version> <spring-boot.version>2.7.9</spring-boot.version> </properties>
<dependencies> <dependency> <groupId>com.xuxueli</groupId> <artifactId>xxl-job-core</artifactId> <version>${xxl-job.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
</project>
|
编写 Java 项目代码
创建应用的主启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.clay.job.executor;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication public class XxlJobExecutorApplication {
public static void main(String[] args) { SpringApplication.run(XxlJobExecutorApplication.class, args); }
}
|
创建 XXL-JOB 的配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| package com.clay.job.executor.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration public class XxlJobConfig {
private static final Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@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() { 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; }
}
|
创建 XXL-JOB 的执行器类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package com.clay.job.executor.jobhandler;
import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component public class CustomJobExecutor {
private static final Logger logger = LoggerFactory.getLogger(CustomJobExecutor.class);
@XxlJob(value = "sampleJobHandler") public void sampleJobHandler() throws Exception { XxlJobHelper.log("XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) { XxlJobHelper.log("beat at:" + i); TimeUnit.SECONDS.sleep(2); }
XxlJobHelper.handleSuccess(); }
}
|
创建项目配置文件
创建日志配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <?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-executor-springboot.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>
|
创建 SpringBoot 配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| server.port=8089
logging.config=classpath:logback.xml
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
xxl.job.accessToken=default_token
xxl.job.executor.appname=xxl-job-executor-springboot
xxl.job.executor.address=
xxl.job.executor.ip=
xxl.job.executor.port=9999
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
xxl.job.executor.logretentiondays=30
|
参数名称 | 参数说明 |
---|
xxl.job.executor.appname | 执行器的名称,建议使用 Maven 模块的名称 |
xxl.job.executor.address | 执行器的注册地址,使用新版本(如 v2.4.0)时,必须带 HTTP/HTTPS 协议头,例如 http://127.0.0.1:9999 |
xxl.job.accessToken | XXL-JOB 的访问令牌,只有调度中心和执行器双方的 AccessToken 互相匹配才允许通讯 |
特别注意
XXL-JOB 从 v.2.3.1
版本开始,调度通讯默认启用 AccessToken
,且默认的 AccessToken
是 default_token
。
测试任务调度代码
启动调度中心服务
调度中心添加执行器
登录 XXL-JOB 调度中心的管理页面,添加自定义的执行器信息,这里的 AppName
是在 application.properties
配置文件中使用 xxl.job.executor.appname
参数指定的。
确定执行器自动注册成功 ,添加执行器后一般需要等待 10 秒左右。
特别注意
XXL-JOB 使用新版本(如 v2.4.0
)时,如果添加执行器选择的是手动录入模式,那么此时填写的机器地址必须是带 HTTP/HTTPS 协议头(例如 http://127.0.0.1:9999
),否则后续添加的调度任务无法正常执行。
调度中心添加调度任务
这里的 JobHandler
配置内容,是在自定义的执行器类里使用 @XxlJob
注解的 value
属性指定。
调度中心启动调度任务
查看任务调度日志信息