XXL-JOB

842 阅读9分钟

image.png

xxl 是作者许雪里名字的首字母缩写,除了 xxl-job 之外,作者还开源了许多其他组件,现在一共有11个开源项目,组成了 xxl 家族

xxl-job 特性

XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展

  • 任务超时、失败、故障转移的处理

  • 提供操作界面、有用户权限、详细的日志、提供通知配置、自动生成报表等等

xxl-job 架构

xxl-job 的调度器和业务执行是独立的。调度器决定任务的调度,并且通过 http 的方式调用执行器接口执行任务

image.png

安装xxl-job

下载源码

下载源码:github.com/xuxueli/xxl…

  • 项目结构(用 2.2.0 分支的稳定版)

image.png

  • doc:文档资料,包括"调度数据库"建表脚本

  • xxl-job-core:公共 Jar 依赖

  • xxl-job-admin:调度中心,项目源码,spring boot 项目,可以直接启动

  • xxl-job-executor-samples:执行器,sample 示例项目,其中的 spring boot 工程,可以直接启动。可以在该项目上进行开发,也可以将现有项目改造生成执行器项目。

初始化数据库

数据库脚本在 doc/db 目录下,执行完以后会生成8张表(检查一下是否执行成功),它们的作用分别如下:

表名作用
xxl_job_group执行器信息表:维护任务执行器信息
xxl_job_info调度扩展信息表:用于保存xxl-job调度任务的扩展信息,如任务分组、任务名、机器地址、执行器、执行入参和报警邮件等等
xxl_job_lock任务调度锁表
xxl_job_log调度日志表:用于保存xxl-job调度任务的历史信息,如调度结果、执行结果、调度入参、调度机器和执行器等等
xxl_job_log_report调度日志报表:用户存储xxl-job任务调度日志的报表,调度中心报表功能页面会用到
xxl_job_logglue任务GLUE日志:用于保存GLUE更新历史,用于支持GLUE的版本回溯功能
xxl_job_registry执行器注册表,维护在线的执行器和调度中心机器地址信息
xxl_job_user系统用户表

安装调度中心

把 xxl-job-admin 单独拷到一个项目出来

image.png

  • 修改 application.properties文件,配置上面初始化的数据库
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=admin
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  • 启动项目

访问:http://127.0.0.1:8080/xxl-job-admin/

默认账号密码:admin/123456

image.png

创建执行器

  • 在源码的 xxl-job-executor-samples 里有6个执行的 demo,告诉你怎么在一些主流框架里面创建执行器,没有框架也可以。执行器可以是一个单独的项目,也可以集成在业务项目里面

image.png

  • 创建一个springboot执行器项目

  • 引入依赖

<!--xxl-job-->
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>2.2.0</version>
</dependency>
  • 将执行器示例xxl-job-executor-sample-springboot下的XxlJobConfig复制到项目下

image.png

  • 配置项目的application.properties文件

    主要配置

    ① xxl-job-admin 调度中心的地址 xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin

    ② 执行器AppNamexxl.job.executor.appname=xxl-job-executor-test

# web port
server.port=${random.int[10000,19999]}
# no web
#spring.main.web-environment=false
# log config
# logging.config=classpath:logback.xml

### 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
### 执行器通讯TOKEN [选填]:非空时启用;
xxl.job.accessToken=
### xxl-job executor appname
### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
xxl.job.executor.appname=xxl-job-executor-test
### 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
### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
xxl.job.executor.ip=
### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
xxl.job.executor.port=${random.int[9000,10000]}
### xxl-job executor log-path
### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
xxl.job.executor.logpath=C:\\Users\\lixb776\\Desktop\\log
### xxl-job executor log-retention-days
### 执行器日志保存天数 [选填] :值大于3时生效,启用执行器Log文件定期清理功能,否则不生效;
xxl.job.executor.logretentiondays=30

注意,因为我们要模拟执行器集群部署,运行多次,为服务设置随机端口,此处配置生成随机端口,真正开发,需要指定固定端口

server.port=${random.int[10000,19999]}
xxl.job.executor.port=${random.int[9000,10000]}

使用xxl-job

添加执行管理器

上一步创建的执行器,不会自动注册到调度中心,需要手动添加

image.png

image.png

AppName 与配置文件中的name保持一直

xxl.job.executor.appname=xxl-job-executor-test

添加任务

  • 写要执行的业务方法(重点:贴 @XxlJob 注解)
@Component
public class DemoJobHandler{
    @XxlJob("firstJob")
    public ReturnT<String> execute(String s) throws Exception {
        XxlJobLogger.log("任务执行。。。。。。"+s);// 注意使用指定方式输出日志
        return ReturnT.SUCCESS;
    }
}
  • 在任务管理中选择刚才添加的执行器

执行器中要执行哪个业务方法,也是需要手动指定,不会自动注册上去的

image.png

  • 添加任务(执行器选择自己添加的,JobHandler 填写@XxlJob("firstJob")注解中的名字),其他参数,下面会详细介绍

image.png

点击立即执行一次

image.png

刷新一下,我这里启动的两个执行器都已经注册上来了,注意,只有节点可以自动注册,执行器和执行的业务方法都需要手动设置

image.png

image.png

添加任务说明

@Component
public class DemoJobHandler{
    @XxlJob("firstJob")
    public ReturnT<String> execute(String s) throws Exception {
        XxlJobLogger.log("任务执行。。。。。。"+s);// 注意使用指定方式输出日志
        return ReturnT.SUCCESS;
    }
}

需要注意的点:

  • 还是需要交给 spring 管理,即要记得贴 @Component

  • 方法格式要求为"public ReturnT < String > demoJobHandler(String param)",返回值和参数格式是固定的,这个是不能动的,唯一能动的是方法名

  • 在方法名上打上 @XxlJob 注解,这里面有几个属性,第一个 value 值对应的是调度中心新建任务的JobHandler属性的值(必填)。另外的 init 对应 JobHandler 初始化方法,destory 对应 JobHandler 销毁方法。这两个方法要在任务类里面创建

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface XxlJob {
    String value();// 必填
    String init() default "";// 非必填
    String destroy() default "";// 非必填
}
  • 执行日志:需要通过"XxlJobLogger.log"打印执行日志,会写到指定的日志文件中。这个是固定规范,任何任务的开发都要执行这个规范,还有上面代码上的开发步骤,这个是许雪里大神在源码中写的

日志位置(可以自定义日志路径):

### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
xxl.job.executor.logpath=C:\\Users\\lixb776\\Desktop\\log

添加任务选项介绍

在上面添加任务的时候,有很多选项需要填写,下面简单介绍这些选项怎么选择

image.png

路由策略

路由策略是指一个任务可以由多个执行器完成,那具体由哪一个完成呢,这就要看我们指定的路由策略了,这个参数当执行器做集群部署的时候才有意义

image.png

点击查看-注册节点中的1,2,3,4,第一个指的就是1,最后一个指的就是4

image.png

运行模式

运行模式分为两种,一种是BEAN(一般都是用这种),一种是GLUE

BEAN模式:这个是在项目中写 Java 类,然后在 JobHandler 里填上 @XxlJob 里面的名字,是在执行器端编写的

GLUE模式:支持Java、Shell、Python、PHP、Nodejs、PowerShell,这个时候代码是直接维护在调度中心这边的

阻塞处理策略

阻塞处理策略指的是任务的一次运行还没有结束,下一次调度的时间又到了,比如一个任务执行的时间是三分钟,但是设置的频率是每两分钟执行一次,这时候第一次还没执行完,第二次怎么办?

一共有三个选项,如下图:

image.png

子任务

  • 当我们要写一个 Job 的时候,任务是相互依赖的。比如下面我要干这么多事情,A执行后再执行B,B执行后再执行C,C执行后再执行D

  • 解决这种问题的时候思路有两种。第一种是把这么多逻辑写成一个大 job,串行化。第二种就是用子任务,在一个任务末尾触发另一个任务

  • 如果我们需要在本任务执行结束并且执行成功的时候触发另外一个任务,那么就可以把另外的任务作为本任务的子任务执行,因为每个 Job 都有自己的唯一 id,所以只需在子任务一栏填上任务 id 即可

任务超时时间

超时的意思就是如果在指定时间内没有返回结果,就不再等待结果

高级任务用法

下面还是对添加任务选项的介绍,由于比较复杂有趣,单独拎出来讲

分片任务

  • 处理大数据时,为了充分发挥分布式优势,可以通过分片任务,每台机子分配处理的唯一一部分数据,防止旱的旱死,涝的涝死

image.png

image.png

  • 分片任务在运行的时候,调度器会给每个执行器发送一个不同的分片序号,分片的最大序号跟执行器的总数量是一样的,确保每个执行器都会执行到这个任务,比如上图中第一个执行器拿到分片序号0,第二台执行器拿到分片序号1,第三台执行器拿到分片序号2;一般通过取余,达到均匀分配的目的。

  • 我们获取数据的sql可以这样写:

//count分片总数,index当前分片数
select id,name,password from student where status=0 and mod(id,#{count})=#{index} order by id desc limit 1000;
  • 在代码中实现这样写
/**
 * 分片广播任务
 * @param param
 * @return
 * @throws Exception
 */
@XxlJob("shardingJob")
public ReturnT<String> shardingJob(String param) throws Exception {
    ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
    System.out.println("当前分片序号:"+ shardingVO.getIndex() +",总分片数:"+ shardingVO.getTotal());
    String sql = "select id,name,password from student where status=0 and mod(id,"+ 
            shardingVO.getTotal() +")="+ shardingVO.getIndex() +" order by id desc limit 1000;";
    //TODO 通过查询sql,拿到需要处理的数据,执行业务方法    
    return ReturnT.SUCCESS;
}
  • 新建任务的时候选择分片广播,填上对应的JobHandler即可

image.png

  • 最后需要说明一下,分片的数据量不一定是完全均等的数据量,上面的取模只是一个举例,一个思路。我们也可以把0、1、2替换成其他条件去从所有数据中获取部分数据,比如分片序号0的机器我查2021年的数据,分片序号1的机器我查2022年的数据,分片序号2的机器我查2023年的数据。具体怎么分看我们的具体业务和机子性能来选择
  • 如果增加或者减少了节点,总分片数和最大分片序号会实时发生变化。

image.png

image.png

总结

  • XXL-JOB是一个分布式任务调度平台
  • 安装 XXL-JOB :初始化项目需要的数据库表、安装调度中心、执行器
  • 使用 XXL-JOB :添加执行器、添加任务