从零到一写组件库---xxlJob组件库

5,423 阅读6分钟

xxlJob简单 介绍

XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。 ———官网

废话不多说,现在我们来开始把xxl-job集成到SpringBoot(SpringCloud)中

SpringBoot集成xxl-job 第一版

下载官方的仓库

没错,你没看错,是把他的仓库克隆下来,和其他的框架不同(在项目中引入jar包即可)

gitee地址:gitee.com/xuxueli0323…

之后idea打开这样

image.png

第二步,配置xxl服务端数据源 连接mysql

xxl-job存储的介质是mysql,所以我们需要执行一下sql文件来创建一个库。找到官方的sql文件

image.png

在我们本地的mysql直接执行,执行之后会得到一个名字叫xxl-job的库

image.png

这里一共八个表,他们的作用是:

  1. xxl_job_user表:该表用于存储用户信息,包括用户的登录名、密码、角色等。用于用户的身份认证和权限管理。
  2. xxl_job_logglue表:该表用于存储任务的日志关联信息,包括任务的执行日志ID、任务的日志时间等。用于关联任务的执行日志和日志文件。
  3. xxl_job_group表:该表用于存储任务分组信息,包括分组的名称、排序等。用于对任务进行分组管理,方便任务的分类和查找。
  4. xxl_job_info表:该表用于存储任务的基本信息,包括任务名称、任务分组、任务描述、任务执行类、任务参数等。每个任务在该表中都有一条记录。
  5. xxl_job_log表:该表用于存储任务的执行日志,包括任务的执行结果、执行时间、执行耗时、执行日志等。每次任务执行完成后,会在该表中插入一条记录。
  6. xxl_job_log_report表:该表用于存储任务的执行报表,包括任务的执行统计信息,如成功次数、失败次数、触发次数、调度时间等。每次任务执行完成后,会在该表中插入或更新一条记录。
  7. xxl_job_registry表:该表用于存储任务执行器的注册信息,包括执行器的地址、端口、心跳时间等。每个执行器在该表中都有一条记录,用于任务的分配和调度。
  8. xxl_job_lock表:该表用于存储任务的分布式锁信息,用于控制任务在分布式环境下的并发执行。每个任务在该表中都有一条记录,用于任务的加锁和解锁。

第三步,连接本地的mysql库

配置文件的地址是:

/xxl-job/xxl-job-admin/src/main/resources/application.properties

这里面我们直接填写本地的数据库信息

image.png

第四步,启动xxl-job-admin项目

配置好本地的mysql环境之后,选择好项目的jdk

之后我们启动启动类

com.xxl.job.admin.XxlJobAdminApplication

控制台打印如下信息则说明我们启动完成了 image.png

访问xxl-job的控制台

http://localhost:8084/xxl-job-admin/toLogin 

默认的用户密码是 admin、123456

image.png

第五步,启动执行器项目

执行器(Executor)是指用于执行具体任务的运行时组件。执行器负责接收任务调度中心分配的任务,并按照任务的配置进行执行。可以理解为xxl-job-admin项目是一个注册中心,而执行器项目就是我们的执行者,注册中心负责管理统筹我们具体的任务,而任务里面具体做了什么事情就是执行器项目里面的业务了。

直接使用官方的项目即可

官方的仓库里面也给了我们一个执行器示例项目,叫做xxl-job-executor-sample-springboot

image.png

这个时候我们去访问我们的控制台页面即可看到我们的执行器项目已经成功注册进来

image.png

5.1 重点 我们看看执行器都做了那些配置接入XXL-Job

  • pom 引入 xxl-job-core
  • application.properties 配置xxlJob 服务端地址
  • XxlJobConfig 配置xxlJob 服务端地址

对于每一个接入 xxl-job的服务都要做上面的三步 所以后续我们会考虑封装个 xxl-stater 简化上述重复步骤呢


# XxlJobConfig


package com.xxl.job.executor.core.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;

/**
 * xxl-job config
 *
 * @author xuxueli 2017-04-28
 */
@Configuration
public class XxlJobConfig {
    private 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;
    }

    /**
     * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
     *
     *      1、引入依赖:
     *          <dependency>
     *             <groupId>org.springframework.cloud</groupId>
     *             <artifactId>spring-cloud-commons</artifactId>
     *             <version>${version}</version>
     *         </dependency>
     *
     *      2、配置文件,或者容器启动变量
     *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
     *
     *      3、获取IP
     *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
     */


}



# application.properties


# web port
server.port=8089
# 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:8084/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=9999
### 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



# POM 

<?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>
    <parent>
        <groupId>com.xuxueli</groupId>
        <artifactId>xxl-job-executor-samples</artifactId>
        <version>2.3.0</version>
    </parent>
    <artifactId>xxl-job-executor-sample-springboot</artifactId>
    <packaging>jar</packaging>

    <name>${project.artifactId}</name>
    <description>Example executor project for spring boot.</description>
    <url>https://www.xuxueli.com/</url>

    <properties>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <!-- Import dependency management from Spring Boot (依赖管理:继承一些默认的依赖,工程需要依赖的jar包的管理,申明其他dependency的时候就不需要version) -->
                <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>

    <dependencies>
        <!-- spring-boot-starter-web (spring-webmvc + tomcat) -->
        <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>

        <!-- xxl-job-core -->
        <dependency>
            <groupId>com.xuxueli</groupId>
            <artifactId>xxl-job-core</artifactId>
            <version>${project.parent.version}</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <!-- spring-boot-maven-plugin (提供了直接运行项目的插件:如果是通过parent方式继承spring-boot-starter-parent则不用此插件) -->
            <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>


第六步,执行定时任务

通过在任务执行类的方法上添加@XxlJob注解,可以将该方法标记为一个定时任务。注解中可以设置任务的名称、分组、CRON表达式等属性,以及任务的参数和路由策略等。

使用项目中的示例任务

com.xxl.job.executor.service.jobhandler.SampleXxlJob#demoJobHandler

image.png

使用控制台调用任务

image.png

之后可以看到代码里面对应的信息被打印到日志中(示例任务是这样的,实际根据你的业务写代码就可以了) image.png

SpringBoot集成xxl-job第二版 组件封装

为了解决上述 [5.1] 提出的问题,我们开始封装 xxlJob stater组件库

组件功能

让公司业务服务快速接入xxl-job 无需额外配置

封装组件源码+注释

组件总概述:

image.png

定义自动引入配置开关

package com.opengoofy.aska12306.springboot.starter.xxljob.annotation;

import com.opengoofy.aska12306.springboot.starter.xxljob.XxlJobAutoConfiguration;
import org.springframework.context.annotation.Import;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * aska: 编写 starter的一种方式,通过定义一个注解,来开启本starter的bean引入
 * <p>
 * 激活xxl-job配置  这是方式二
 *
 * @date 2020/9/14
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({XxlJobAutoConfiguration.class})
public @interface EnableXxlJob {

}
package com.opengoofy.aska12306.springboot.starter.xxljob.properties;

import lombok.Data;

/**
 * xxl-job管理平台配置
 *
 *
 * @date 2020/9/14
 */
@Data
public class XxlAdminProperties {

   /**
    * 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。 执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
    */
   private String addresses;

}
package com.opengoofy.aska12306.springboot.starter.xxljob.properties;

import lombok.Data;

/**
 * xxl-job执行器配置
 *
 *
 * @date 2020/9/14
 */
@Data
public class XxlExecutorProperties {

   /**
    * 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
    */
   private String appname;

   /**
    * 服务注册地址,优先使用该配置作为注册地址 为空时使用内嵌服务 ”IP:PORT“ 作为注册地址 从而更灵活的支持容器类型执行器动态IP和动态映射端口问题
    */
   private String address;

   /**
    * 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP ,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和
    * "调度中心请求并触发任务"
    */
   private String ip;

   /**
    * 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9099,单机部署多个执行器时,注意要配置不同执行器端口;
    */
   private Integer port = 0;

   /**
    * 执行器通讯TOKEN [必填]:从配置文件中取不到值时使用默认值;
    */
   private String accessToken = "";

   /**
    * 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
    */
   private String logPath = "logs/applogs/xxl-job/jobhandler";

   /**
    * 执行器日志保存天数 [选填] :值大于3时生效,启用执行器Log文件定期清理功能,否则不生效;
    */
   private Integer logRetentionDays = 30;

}
package com.opengoofy.aska12306.springboot.starter.xxljob.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

/**
 * xxl-job配置
 *
 *
 * @date 2020/9/14
 */
@Data
@ConfigurationProperties(prefix = "xxl.job")
public class XxlJobProperties {

   @NestedConfigurationProperty
   private XxlAdminProperties admin = new XxlAdminProperties();

   @NestedConfigurationProperty
   private XxlExecutorProperties executor = new XxlExecutorProperties();

}

xxl-job自动装配 core

package com.opengoofy.aska12306.springboot.starter.xxljob;

import com.opengoofy.aska12306.springboot.starter.xxljob.properties.XxlExecutorProperties;
import com.opengoofy.aska12306.springboot.starter.xxljob.properties.XxlJobProperties;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.stream.Collectors;

/**
 * xxl-job自动装配
 *
 * @date 2020/9/14
 */
@EnableConfigurationProperties(XxlJobProperties.class)
public class XxlJobAutoConfiguration {
    private Logger log = LoggerFactory.getLogger(XxlJobAutoConfiguration.class);

    /**
     * 服务名称 包含 XXL-JOB 则说明是 Admin
     */
    private static final String XXL_JOB_ADMIN = "xxl-job-admin";

    /**
     * 配置xxl-job 执行器,提供自动发现 xxl-job 能力
     *
     * @param xxlJobProperties xxl 配置
     * @param environment      环境变量
     * @param discoveryClient  注册发现客户端
     * @return
     */
    @Bean
    public XxlJobSpringExecutor xxlJobSpringExecutor(XxlJobProperties xxlJobProperties, Environment environment, DiscoveryClient discoveryClient) {
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        XxlExecutorProperties executor = xxlJobProperties.getExecutor();
        // 应用名默认为服务名
        String appName = executor.getAppname();
        if (!StringUtils.hasText(appName)) {
            appName = environment.getProperty("spring.application.name");
        }
        String accessToken = environment.getProperty("xxl.job.accessToken");
        if (!StringUtils.hasText(accessToken)) {
            accessToken = executor.getAccessToken();
        }

        xxlJobSpringExecutor.setAppname(appName);
        xxlJobSpringExecutor.setAddress(executor.getAddress());
        xxlJobSpringExecutor.setIp(executor.getIp());
        xxlJobSpringExecutor.setPort(executor.getPort());
        xxlJobSpringExecutor.setAccessToken(accessToken);
        xxlJobSpringExecutor.setLogPath(executor.getLogPath());
        xxlJobSpringExecutor.setLogRetentionDays(executor.getLogRetentionDays());

        // 配置
        // nacos 如果配置为空则获取注册中心的服务列表 "http://ip:8080/xxl-job-admin"
        if (!StringUtils.hasText(xxlJobProperties.getAdmin().getAddresses())) {
            List<String> serviceList = discoveryClient.getServices();
            String serverList = serviceList.stream()
                    .filter(s -> s.contains(XXL_JOB_ADMIN))
                    .flatMap(s -> discoveryClient.getInstances(s).stream()).map(instance -> String.format("http://%s:%s/%s", instance.getHost(), instance.getPort(), XXL_JOB_ADMIN))
                    .collect(Collectors.joining(","));
            xxlJobSpringExecutor.setAdminAddresses(serverList);
        } else {
            xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdmin().getAddresses());
        }
        log.info("init-xxlJobSpringExecutor: " + xxlJobSpringExecutor.toString());
        return xxlJobSpringExecutor;
    }

}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.opengoofy.aska12306.springboot.starter.xxljob.XxlJobAutoConfiguration

//aska: 编写 starter的一种方式,通过spring.factories,来自动开启本starter的bean引入
<?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">
    <parent>
        <artifactId>aska12306-frameworks</artifactId>
        <groupId>com.aska12306</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>aska12306-xxljob-spring-boot-starter</artifactId>

    <description>定时任务,基于xxl-job</description>

    <dependencies>
        <dependency>
            <groupId>com.xuxueli</groupId>
            <artifactId>xxl-job-core</artifactId>
            <version>${xxl-job.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

</project>

总结

通过本文的介绍,我们了解了如何在Spring Boot项目中集成XXL-Job,实现超牛的定时任务。XXL-Job提供了强大的任务调度和管理功能,使得定时任务的开发和管理变得更加简单和高效。我们还封装了starter组件,方便公司内部服务集成。提供统一接口

集成XXL-Job,我们可以通过配置或注解的方式定义定时任务,并灵活地设置任务的调度规则和执行方式。无论是按照时间间隔还是时间点执行任务,XXL-Job都能满足我们的需求。同时,XXL-Job还提供了任务的监控和管理功能,让我们可以实时了解任务的执行情况和结果。

通过使用XXL-Job,我们可以轻松实现定时任务的自动化执行,提高系统的稳定性和可靠性。无论是在企业级应用中还是个人项目中,XXL-Job都能为我们带来便利和效益。

XXL-Job作为一款国产的优秀开源软件,提供了强大的任务调度和管理功能,完全能够媲美甚至超越国外同类产品。作为国产软件的使用者和推广者,我们应该积极拥抱国产软件,为其发展壮大贡献自己的力量。让我们携手努力,为国内软件行业的发展做出更大的贡献(没收许雪里的钱)!