一、背景
本文档主要介绍xxl-job的基本原理和在springboot项目中的应用。
选型参考文档:
http://docs.fscut.com/pages/viewpage.action?pageId=80192125
非常非常非常详细的保姆级官方文档,强烈建议直接看这个:
https://www.xuxueli.com/xxl-job/
二、基本原理
如下图所示是XXL-Job V2.2.0的架构图。
主要分为调度中心和执行器两个部分。
2.1调度中心
调度中心是一个独立的项目,它的核心功能就是发现执行器、触发定时任务。除此之外,还包含任务管理、执行器管理、日志管理等模块。
调度中心的其他描述请看第四节。
2.2执行器
执行器就是执行定时任务的机器,也就是需要与业务项目整合的部分。
定时任务的【任务】就是执行器的主要内容,定时任务的【定时】配置如时间、阻塞策略等均在调度中心进行配置,从而实现了两者的解耦。
执行器所在项目运行时,会随之启动一个Server,会被调度中心自动发现和远程调用。
执行器的配置请看第五节。
执行器的任务注册请看第六节。
三、源码概览
源码分为三个部分:
调度中心项目代码、执行器MAVEN依赖的源码、执行器demo项目。
调度中心的数据库表结构在/doc/db/tables_xxl_job.sql
四、调度中心
4.1基础配置
和其他springboot项目一样,配置信息在application.yml文件中,如下所示。
### 调度中心JDBC链接
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
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
### 调度中心通讯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
4.2项目部署
调度中心默认访问地址:
http://localhost:8080/xxl-job-admin
调度中心默认登录账户:
admin/123456
调度中心的交互页面:
五、执行器配置
5.1maven依赖
<!-- https://mvnrepository.com/artifact/com.xuxueli/xxl-job-core -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.2.0</version>
</dependency>
5.2执行器配置
关键配置:
调度中心的地址、执行器的APPNAME、执行器的端口(多实例必须,默认9999)、
### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### xxl-job, access token
xxl.job.accessToken=
### xxl-job executor appname
xxl.job.executor.appname=xxl-job-executor-sample
### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null
xxl.job.executor.address=
### xxl-job executor server-info
xxl.job.executor.ip=
xxl.job.executor.port=9998
### xxl-job executor log-path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job executor log-retention-days
xxl.job.executor.logretentiondays=30
5.3执行器组件配置
参考代码如下所示,或于执行器demo项目中查看。
@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(">>>>>>>>>>> kb 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;
}
}
至此,执行器项目就配置好了。
下一节开发第一个定时任务。
六、开发第一个定时任务
任务的运行模式主要分成两种:BEAN模式和GLUE模式。
6.1bean demo
在执行器配置所在的项目中新增如下实现。
@Slf4j
@Component
public class ScheduleService {
@XxlJob("kbJobHandler")
public ReturnT<String> kbJobHandler(String param) {
log.info("kb job handler");
log.info(param);
XxlJobLogger.log("XXL-JOB, Hello World.");
return ReturnT.SUCCESS;
}
}
1.通过注解@XxlJob注册任务BEAN,对应调度中心配置任务时填写的JobHandler。
2.方法的参数“param”即为调度中心配置任务时填写的任务参数。
3.返回值是指任务执行成功或失败。
4.XxlJobLogger会将日志写入调度中心的执行日志。
6.2demo的完整演示步骤
1.启动xxl-job-admin项目,开启调度中心服务。
2.启动执行器所在项目。
3.访问http://localhost:8080/xxl-job-admin
4.在执行器管理模块,新增执行器。
AppName:填写 5.2 中配置的appname;
名称:执行器在调度中心的名称,自行命名即可,此处为A;
机器地址:选择自动注册。
5.执行器新增后,10s内刷新会看到【Online机器地址】下出现【查看(1)】字样,说明执行器项目已被自动识别。
6.在任务管理模块,选择刚刚新建的执行器A,新增任务。
任务描述:定时任务在调度中心的名称,自行命名即可,此处为a;
调度类型:CRON;
Cron:填写好时间表达式;
运行模式:BEAN;
JobHandler:填写 6.1 注解 @XxlJob 中的值,此处为kbJobHandler;
其他参数默认即可。
7.启动任务,可以看到状态栏由STOP变为RUNNING。
8.在调度日志模块可查看执行过的任务日志。
七、任务详解
7.1BEAN
BEAN模式可通过两种方式注册:类形式和方法形式。
类型 | 优点 | 缺点 |
---|---|---|
类形式 | 不限制项目形式,非spring项目可用,即使是main直接启动的项目也可用 | 不支持自动扫描,需要手动写入执行器的机器地址 |
方法形式 | 支持自动扫描,不需要手动写入执行器机器地址 | 必须是Spring容器环境 |
两者的本质都是实现了IJobHandler接口,然后注入到执行器容器中。
// 实现IJobHandler的方法
public class MethodJobHandler extends IJobHandler {
// override implements
}
// 注入执行器
registJobHandler(name, new MethodJobHandler(bean, executeMethod, initMethod, destroyMethod));
public static IJobHandler registJobHandler(String name, IJobHandler jobHandler){
logger.info(">>>>>>>>>>> xxl-job register jobhandler success, name:{}, jobHandler:{}", name, jobHandler);
return jobHandlerRepository.put(name, jobHandler);
}
IJobHandler如下所示。
public abstract class IJobHandler {
public abstract void execute() throws Exception;
public void init() throws Exception {
// do something
}
public void destroy() throws Exception {
// do something
}
}
由init和destroy可推测执行器允许在执行前后调用其他实现,如下所示。
@XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy")
public void demoJobHandler2() throws Exception {
XxlJobHelper.log("XXL-JOB, Hello World.");
}
public void init(){
logger.info("init");
}
public void destroy(){
logger.info("destory");
}
下边是一个类形式的demo,方法形式可看第六节的demo。
public void initXxlJobExecutor() {
// 执行器配置
Properties xxlJobProp = loadProperties("xxl-job-executor.properties");
xxlJobExecutor = new XxlJobSimpleExecutor();
xxlJobExecutor.setAdminAddresses(xxlJobProp.getProperty("xxl.job.admin.addresses"));
xxlJobExecutor.setAccessToken(xxlJobProp.getProperty("xxl.job.accessToken"));
xxlJobExecutor.setAppname(xxlJobProp.getProperty("xxl.job.executor.appname"));
xxlJobExecutor.setAddress(xxlJobProp.getProperty("xxl.job.executor.address"));
xxlJobExecutor.setIp(xxlJobProp.getProperty("xxl.job.executor.ip")); xxlJobExecutor.setPort(Integer.valueOf(xxlJobProp.getProperty("xxl.job.executor.port")));
xxlJobExecutor.setLogPath(xxlJobProp.getProperty("xxl.job.executor.logpath"));
xxlJobExecutor.setLogRetentionDays(Integer.valueOf(xxlJobProp.getProperty("xxl.job.executor.logretentiondays")));
// 任务bean注入执行器
xxlJobExecutor.setXxlJobBeanList(Arrays.asList(new SampleXxlJob()));
// 启动执行器
try {
xxlJobExecutor.start();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
任务Bean的实现与方法模式类似。
7.2GLUE(JAVA/Shell/Python/PHP/Nodejs/PowerShell)
在调度中心任务管理模块创建一个GLUE(JAVA)的任务,在这个任务的【操作】菜单栏下就可以打开【GLUE IDE】,如下所示。
GLUE任务的源码维护在调度中心,通过Web IDE在线更新,实时编译和生效,因此不需要指定JobHandler。
值得注意的是,虽然GLUE源码维护在调度中心,但是需要指定执行器,且脚本的执行是在执行器项目下的。
如下图所示,新建GLUE(Python)脚本。
可以看到运行的路径是执行器的根目录下。
八、总结
至此,Xxl-Job的基本功能已介绍完毕。
Xxl-Job的架构为1个调度中心与N个执行器,通过自研RPC进行通信;
执行器的任务可以是Java-Bean,也可以是GLUE脚本,GLUE脚本支持Shell、Python、PHP等多种形式;
GLUE脚本的维护在调度中心,执行在执行器。