SpringBoot 整合 XXL-JOB 详解

550 阅读6分钟

SpringBoot 整合 XXL-JOB 详解

本文将详细介绍如何在 SpringBoot 项目中整合 XXL-JOB 分布式任务调度平台,包括调度中心部署、执行器配置、任务开发和管理等内容。

一、XXL-JOB 简介

XXL-JOB 是一个轻量级分布式任务调度平台,具有以下特点:

  • 简单易用,学习成本低
  • 功能丰富,支持多种任务调度方式
  • 高可用,支持集群部署
  • 丰富的监控和报警机制
  • 易于扩展

二、环境准备

1. 基础环境要求

  • JDK 1.8+
  • Maven 3+
  • MySQL 5.7+

2. 下载 XXL-JOB 源码

从官方仓库下载源码:

# GitHub
git clone https://github.com/xuxueli/xxl-job.git

# 或 Gitee
git clone http://gitee.com/xuxueli0323/xxl-job.git

# 切换到稳定版本(以2.3.0为例)
git checkout -b 2.3.0 origin/2.3.0

三、调度中心部署

1. 初始化数据库

执行源码中 /xxl-job/doc/db/tables_xxl_job.sql 脚本,创建 xxl_job 数据库和相关表。

2. 配置调度中心

修改 xxl-job-admin 模块的 application.properties 文件:

# 服务端口
server.port=8080

# 数据库连接信息
spring.datasource.url=jdbc:mysql://localhost:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# 登录账号配置
xxl.job.login.username=admin
xxl.job.login.password=123456

# 调度中心通信TOKEN(可选,非空时启用)
xxl.job.accessToken=default_token

# 日志配置
logging.config=classpath:logback.xml

3. 启动调度中心

编译并启动 xxl-job-admin 项目:

cd xxl-job/xxl-job-admin
mvn clean package
java -jar target/xxl-job-admin.jar

访问 http://localhost:8080/xxl-job-admin,使用默认账号密码登录:

  • 用户名:admin
  • 密码:123456

四、SpringBoot 项目整合 XXL-JOB

1. 创建 SpringBoot 项目

可以使用 Spring Initializr 创建一个基础的 SpringBoot 项目。

2. 添加依赖

在项目的 pom.xml 文件中添加 XXL-JOB 依赖:

<!-- XXL-JOB 核心依赖 -->
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>2.3.0</version> <!-- 版本需与调度中心一致 -->
</dependency>

3. 配置文件设置

application.propertiesapplication.yml 中配置 XXL-JOB:

application.yml 配置:
server:
  port: 8081

spring:
  application:
    name: xxl-job-executor-sample

# XXL-JOB 配置
xxl:
  job:
    admin:
      addresses: http://localhost:8080/xxl-job-admin  # 调度中心地址
    accessToken: default_token  # 与调度中心的通信令牌
    executor:
      appname: xxl-job-executor-sample  # 执行器名称
      ip:  # 执行器IP,默认为空自动获取
      port: 9999  # 执行器端口
      logpath: /data/applogs/xxl-job/jobhandler  # 日志路径
      logretentiondays: 30  # 日志保留天数

# 日志配置
logging:
  config: classpath:logback.xml

4. 创建 XXL-JOB 配置类

package com.example.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;

@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.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.setIp(ip);
        xxlJobSpringExecutor.setPort(port);
        xxlJobSpringExecutor.setAccessToken(accessToken);
        xxlJobSpringExecutor.setLogPath(logPath);
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);

        return xxlJobSpringExecutor;
    }
}

5. 创建任务处理器

创建一个任务处理器类,使用 @XxlJob 注解定义任务:

package com.example.jobhandler;

import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class SampleXxlJob {
    private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class);

    /**
     * 1. 简单任务示例(Bean模式)
     */
    @XxlJob("demoJobHandler")
    public ReturnT<String> demoJobHandler(String param) throws Exception {
        logger.info("XXL-JOB, Hello World.");
        
        for (int i = 0; i < 5; i++) {
            logger.info("beat at {}, param: {}", i, param);
            TimeUnit.SECONDS.sleep(2);
        }
        return ReturnT.SUCCESS;
    }

    /**
     * 2. 分片广播任务
     */
    @XxlJob("shardingJobHandler")
    public ReturnT<String> shardingJobHandler(String param) throws Exception {
        // 分片参数
        int shardIndex = XxlJobHelper.getShardIndex();
        int shardTotal = XxlJobHelper.getShardTotal();

        logger.info("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal);
        
        // 业务逻辑
        for (int i = 0; i < 5; i++) {
            logger.info("第 {} 片, 第 {} 次任务执行", shardIndex, i);
            TimeUnit.SECONDS.sleep(1);
        }

        return ReturnT.SUCCESS;
    }

    /**
     * 3. 命令行任务
     */
    @XxlJob("commandJobHandler")
    public ReturnT<String> commandJobHandler(String param) throws Exception {
        String command = param;
        if (command == null || command.trim().length() == 0) {
            command = "echo 'hello xxl-job'";
        }

        Process process = Runtime.getRuntime().exec(command);
        String result = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8);
        String error = IOUtils.toString(process.getErrorStream(), StandardCharsets.UTF_8);
        int exitValue = process.waitFor();

        logger.info("command: {}, result: {}, error: {}, exitValue: {}", command, result, error, exitValue);

        if (exitValue == 0) {
            return ReturnT.SUCCESS;
        } else {
            return new ReturnT<String>(ReturnT.FAIL_CODE, "command execute failed, exitValue=" + exitValue);
        }
    }
}

五、在调度中心配置执行器和任务

1. 配置执行器

  1. 登录调度中心(http://localhost:8080/xxl-job-admin)
  2. 点击左侧菜单「执行器管理」->「新增」
  3. 填写执行器信息:
    • 执行器AppName:xxl-job-executor-sample(与SpringBoot配置中的appname保持一致)
    • 执行器名称:示例执行器
    • 注册方式:自动注册
    • 机器地址:留空(自动注册时不需要填写)

2. 配置任务

  1. 点击左侧菜单「任务管理」->「新增」
  2. 填写任务信息:
    • 执行器:选择刚刚创建的「示例执行器」
    • 任务描述:Demo任务
    • 调度类型:CRON
    • CRON:0/30 * * * * ?(每30秒执行一次)
    • 运行模式:BEAN
    • JobHandler:demoJobHandler(与@XxlJob注解中的值保持一致)
    • 阻塞处理策略:单机串行
    • 任务参数:可填写自定义参数

3. 启动任务

  1. 在任务管理列表中找到刚刚创建的任务
  2. 点击「操作」列的「启动」按钮
  3. 查看任务执行日志,确认任务正常运行

六、XXL-JOB 高级功能

1. 任务参数传递

@XxlJob("demoJobHandler")
public ReturnT<String> demoJobHandler(String param) throws Exception {
    logger.info("接收到的任务参数: {}", param);
    // 业务逻辑处理
    return ReturnT.SUCCESS;
}

2. 任务失败重试

调度中心配置中,可以设置任务失败后的重试次数和重试间隔。

3. 分片任务

适用于大数据量的任务处理场景,将一个大任务分成多个小任务在不同机器上并行执行:

@XxlJob("shardingJobHandler")
public ReturnT<String> shardingJobHandler(String param) throws Exception {
    // 获取分片参数
    int shardIndex = XxlJobHelper.getShardIndex();  // 当前分片索引
    int shardTotal = XxlJobHelper.getShardTotal();  // 总分片数
    
    logger.info("分片参数:当前分片 = {}, 总分片 = {}", shardIndex, shardTotal);
    
    // 根据分片索引处理对应的数据范围
    // 例如:处理数据库中 id % shardTotal == shardIndex 的数据
    
    return ReturnT.SUCCESS;
}

4. 任务依赖

可以配置任务的前置依赖,确保任务按照指定顺序执行。

七、常见问题及解决方案

1. 执行器注册失败

  • 检查调度中心地址是否正确
  • 检查 accessToken 是否与调度中心一致
  • 检查执行器端口是否被占用
  • 查看日志中的具体错误信息

2. 任务执行失败

  • 检查任务处理器中的业务逻辑
  • 查看执行器日志中的错误信息
  • 确认任务参数格式正确

3. 任务阻塞

  • 合理设置任务的执行时间
  • 选择合适的阻塞处理策略
  • 对于长时间运行的任务,考虑拆分为多个小任务

八、最佳实践

  1. 合理设置任务调度频率:避免过于频繁的任务执行导致系统负载过高
  2. 任务幂等性设计:确保任务重复执行不会产生副作用
  3. 任务超时控制:设置合理的任务超时时间,避免任务无限执行
  4. 资源隔离:不同类型的任务尽量部署在不同的执行器上
  5. 监控和报警:配置任务执行失败的报警机制
  6. 日志管理:定期清理任务日志,避免磁盘空间不足

九、完整的项目结构

src/
├── main/
│   ├── java/com/example/
│   │   ├── config/
│   │   │   └── XxlJobConfig.java         # XXL-JOB 配置类
│   │   ├── jobhandler/
│   │   │   └── SampleXxlJob.java         # 任务处理器
│   │   └── XxlJobExecutorApplication.java # 应用入口
│   └── resources/
│       ├── application.yml               # 配置文件
│       └── logback.xml                   # 日志配置
└── pom.xml                               # Maven 配置

以上就是 SpringBoot 整合 XXL-JOB 的完整实现方案。通过这种方式,您可以轻松地在 SpringBoot 项目中实现分布式任务调度,提高系统的可靠性和可维护性。