xxl-job的简单入门

前言

在写这篇文章之前,我们有一个单独的任务调度项目,这个项目按照cron表达式,隔一段时间执行一下项目的API。目前来看如果项目不大的话用这种方式也是挺好的,对项目侵入性低,也很灵活。不过由于这个任务调度项目功能过于简单(没有告警,没有页面可以方便的查看日志,权限管理等),也没有足够的文档。所以我们干脆直接找一个开源的分布式任务调度框架。在对比之后选了xxl-job,看上的是它的轻量和容易上手,而且上述的要求它都能满足。

xxl-job概念

首先先介绍一些xxl-job的概念:

  • 调度中心:统一管理任务调度平台上调度任务,负责触发调度执行,并且提供任务管理平台。也就是执行器的触发角色
  • 执行器:执行器就是我们需要执行的任务项目了,它由调度中心来负责调用

调度中心搭建

搭建方式有两种,一种是拉取git仓库的源码(SpringBoot项目),修改SpringBoot的配置文件打包运行就好了

我选的另一种搭建方式,使用Docker的方式来搭建,先上docker-compose.yml文件,一看基本上就明白了

version: '3.5'

services: 
  xxl-job-mysql:
    image: mysql:8.0
    container_name: xxl-job-mysql
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_PASS}
    volumes:
      - ${VOLUME_PATH}/xxl-job-mysql:/var/lib/mysql
      - ./xxl-job.sql:/docker-entrypoint-initdb.d/xxl-job.sql
    restart: always
    networks:
      - kazma_net
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
  xxl-job:
    image: xuxueli/xxl-job-admin:2.2.0
    container_name: my-xxl-job
    restart: always
    ports: 
      - 9010:8080
    volumes: 
      - ${VOLUME_PATH}/xxl-job/applogs:/data/applogs
    networks: 
      - kazma_net
    environment: 
      TZ: Asia/Shanghai
      PARAMS: "--spring.datasource.url=jdbc:mysql://xxl-job-mysql:3306/xxl_job \ 
        --spring.datasource.username=root \ 
        --spring.datasource.password=pw123456 \
        --spring.mail.host=smtp.163.com \ 
        --spring.mail.username=kazma_todo@163.com \
        --spring.mail.password="
    depends_on: 
      - xxl-job-mysql


networks:
  kazma_net:
    name: kazma_network

其中xxl-job.sql文件是xxl-job官方的sql,需要初始化数据库设置。放入Dockerdocker-entrypoint-initdb.d目录下,该sql文件会自动执行,后续初始化后,就不会再执行了。可以参考我的github配置

另外environment下的配置也可以参看官方的Dockerfile文件:

FROM openjdk:8-jre-slim
MAINTAINER xuxueli

ENV PARAMS=""

ENV TZ=PRC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

ADD target/xxl-job-admin-*.jar /app.jar

ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /app.jar $PARAMS"]
  • 这里把PARAMS的参数传递给了项目,具体参数可以参考官方github上的application.properties文件`
  • TZ是写入到了系统的时区上了

这样访问就可以访问了:http://192.168.1.233:9010/xxl-job-admin,端口是9010,路径是xxl-job-admin,界面如下:

xxx-job首页

默认的用户名是: admin 密码是: 123456

编写执行器

在启动好了调度中心后,会有一个默认的执行器,但是这个只是单纯的配置了,并没实际的代码实现。我们现在就来实现一个,不过我们不会用他这个名字。

新建SpringBoot项目:

  • 项目依赖如下:

    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
    	<groupId>org.projectlombok</groupId>
    	<artifactId>lombok</artifactId>
    	<optional>true</optional>
    </dependency>
    
    <dependency>
    	<groupId>com.xuxueli</groupId>
    	<artifactId>xxl-job-core</artifactId>
    	<version>2.2.0</version>
    </dependency>
    

    主要是xxl-job-core这个依赖

  • 项目配置如下:

    我单纯的把官方的配置转换成了yml的格式

     server:
       port: 9200
    
     logging:
       level:
         root: debug
    
     xxl:
       job:
         ### 执行器通讯TOKEN [选填]:非空时启用;
         access-token:
         admin:
         ### 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
         addresses: http://192.168.1.233:9010/xxl-job-admin
         executor:
         ### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
         appname: demo-job
         ### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。
         address:
         ### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
         ip:
         ### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
         port: 9999
         ### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
         logpath: ./applogs/xxl-job
         ### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
         logretentiondays: 30   
    
  • 接下来是初始化xxl-job

    XxlJobConfig.java文件

     package com.xxljob.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;
     import org.springframework.stereotype.Component;
    
     @Component
     @Configuration
     public class XxlJobConfig {
    
         private static final Logger LOGGER = LoggerFactory.getLogger(XxlJobConfig.class);
    
         @Value("${xxl.job.admin.addresses}")
         private String adminAddresses;
    
         @Value("${xxl.job.access-token}")
         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;
         }
    
     }
    
    

    这里读取了application.yml的配置,初始化了一个XxlJobSpringExecutor对象。

  • 真正的任务执行器代码

    这其实就是一个service

     @XxlJob("xxl-job-handler")
     public ReturnT<String> execute(String param) {
         XxlJobLogger.log("hello world.");
    
         return ReturnT.SUCCESS;
     }
    

    官方解释,我直接摘抄过来了

    1、在Spring Bean实例中,开发Job方法,方式格式要求为 "public ReturnT<String> execute(String param)"
    2、为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。
    3、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;

以上就是整个项目的代码了,可以启动,并且查看日志。

2020-05-16 19:43:22.410 DEBUG 51880 --- [rRegistryThread] c.x.j.c.thread.ExecutorRegistryThread    : >>>>>>>>>>> xxl-job registry success, registryParam:RegistryParam{registryGroup='EXECUTOR', registryKey='demo-job', registryValue='http://192.168.1.105:9999/'}, registryResult:ReturnT [code=200, msg=null, content=null]

可以看到本地的任务地址是http://192.168.1.105:9999/,也就是我们配置的xxl-job配置上的,不是SpringBoot项目的9200端口,这个要注意。然后demo-job是执行器的名称

配置任务调度中心

  • 配置执行器 重新回到xxl-job的任务调度中心web页面,左侧点击执行器管理,新增一个执行器。这里的AppName就是我们代码配置的xxl.job.executor.appname,然后选自动注册就好了。

    新增执行器

    执行器结果

  • 任务管理 点击左侧的任务管理,新增一个任务,具体的解释可以看官方文档 任务管理

    然后在当前任务的右侧点击下拉箭头,单机运行一次,可以在执行器项目的控制台上看到日志的输出,并且在任务调度中心调度日志上也能看到具体的调用日志。

    任务执行 任务日志

    想要正式的启动,就在任务管理的单个任务上,单机下拉箭头,点击启动即可。后续可以观察日志或者设置告警邮件来检查定时任务的执行过程。

    任务启动前 任务启动

总结

像是邮件告警,权限控制这些我并没有特意的去说明,因为这些太简单了,在界面上点点就能懂。要是有什么疑问也可以看官方的文档解决。

xxl-job我们也刚开始用,后续如果有什么优化,或者更进阶的使用,我也会用新的文章写上来,总之会把我们现在的任务调度服务给慢慢替换掉

人生苦短,保持简单才是一个项目持久的王道