XXL-JOB > Tour

一、背景

本文档主要介绍xxl-job的基本原理和在springboot项目中的应用。

选型参考文档:

http://docs.fscut.com/pages/viewpage.action?pageId=80192125

非常非常非常详细的保姆级官方文档,强烈建议直接看这个:

https://www.xuxueli.com/xxl-job/

二、基本原理

如下图所示是XXL-Job V2.2.0的架构图。

xxl-jobV2.2.0架构图

主要分为调度中心和执行器两个部分。

2.1调度中心

调度中心是一个独立的项目,它的核心功能就是发现执行器、触发定时任务。除此之外,还包含任务管理、执行器管理、日志管理等模块。

调度中心的其他描述请看第四节。

2.2执行器

执行器就是执行定时任务的机器,也就是需要与业务项目整合的部分。

定时任务的【任务】就是执行器的主要内容,定时任务的【定时】配置如时间、阻塞策略等均在调度中心进行配置,从而实现了两者的解耦。

执行器所在项目运行时,会随之启动一个Server,会被调度中心自动发现和远程调用。

执行器的配置请看第五节。

执行器的任务注册请看第六节。

三、源码概览

源码分为三个部分:

调度中心项目代码、执行器MAVEN依赖的源码、执行器demo项目。

调度中心的数据库表结构在/doc/db/tables_xxl_job.sql

xxl-job源码预览

四、调度中心

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

调度中心的交互页面:

xxl-job2

xxl-job3

xxl-job4

五、执行器配置

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”即为调度中心配置任务时填写的任务参数。

xxl-job6

3.返回值是指任务执行成功或失败。

xxl-job7

4.XxlJobLogger会将日志写入调度中心的执行日志。

xxl-job8

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】,如下所示。

xxl-job9

GLUE任务的源码维护在调度中心,通过Web IDE在线更新,实时编译和生效,因此不需要指定JobHandler。

值得注意的是,虽然GLUE源码维护在调度中心,但是需要指定执行器,且脚本的执行是在执行器项目下的。

如下图所示,新建GLUE(Python)脚本。

xxl-job10

xxl-job11

xxl-job12

可以看到运行的路径是执行器的根目录下。

xxl-job13

八、总结

至此,Xxl-Job的基本功能已介绍完毕。

Xxl-Job的架构为1个调度中心与N个执行器,通过自研RPC进行通信;

执行器的任务可以是Java-Bean,也可以是GLUE脚本,GLUE脚本支持Shell、Python、PHP等多种形式;

GLUE脚本的维护在调度中心,执行在执行器。