欢迎光临
我们一直在努力

XXL-JOB调度中心从入门到实战:分布式任务调度一文搞定

本文适合刚接触分布式任务调度的开发者,以及准备面试的同学。从核心概念到实战部署,代码简洁易懂,快速上手企业级调度中心。


一、什么是调度中心?

一句话: 调度中心就是"定时任务管理平台",统一管理和监控所有定时任务。

传统方式:                     调度中心方式:
┌─────────┐                   ┌─────────────────────┐
│ 服务A   │ @Scheduled       │     调度中心        │
│ 服务B   │ cron表达式        │   (XXL-JOB)         │
│ 服务C   │ 分散在各服务中    │         ↓         │
└─────────┘                   │统一管理、监控、报警 │
                            └─────────────────────┘
                                    ↓
                            ┌─────────────────────┐
                            │服务A 服务B 服务C │
                            └─────────────────────┘

常见调度中心对比:

调度中心特点适用场景
Spring Task 简单、单机 简单定时任务
Quartz 功能强大、复杂 单体应用
XXL-JOB 可视化、分布式、易用 企业级首选(推荐)
Elastic-Job 分片、弹性 大数据量任务

二、XXL-JOB核心架构

┌─────────────────────────────────────────────────────────────┐
│                       调度中心(Admin)                    │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐       │
│ │任务管理 │ │调度日志 │ 执行器管理│ │用户管理 │   │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘       │
└─────────────────────────────────────────────────────────────┘
                            ↓ 调度触发
┌─────────────────────────────────────────────────────────────┐
│                       执行器(Executor)                   │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐         │
│ │服务A执行器 │ │服务B执行器 │ │服务C执行器 │         │
│ └─────────────┘ └─────────────┘ └─────────────┘         │
└─────────────────────────────────────────────────────────────┘

核心概念:

  • 调度中心:任务管理后台,负责触发任务

  • 执行器:实际执行任务的服务,注册到调度中心

  • 任务:具体的业务逻辑(一个Java方法)


三、快速部署(10分钟搞定)

3.1 下载源码

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

# 目录结构
xxl-job/
├── doc/                    # 文档和SQL脚本
├── xxl-job-admin/          # 调度中心(后台管理)
└── xxl-job-executor-samples/  # 执行器示例

3.2 初始化数据库

— 执行 doc/db/tables_xxl_job.sql
— 创建8张表:

— 1. xxl_job_info:任务信息
— 2. xxl_job_log:调度日志
— 3. xxl_job_log_report:报表
— 4. xxl_job_logglue:GLUE日志
— 5. xxl_job_group:执行器分组
— 6. xxl_job_user:用户
— 7. xxl_job_lock:分布式锁

3.3 启动调度中心

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

server.port=8080
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456

# 调度中心通讯TOKEN(执行器需要配置相同TOKEN)
xxl.job.accessToken=xxl-job-token

启动 XxlJobAdminApplication,访问 http://localhost:8080

  • 账号:admin

  • 密码:123456


四、创建执行器(Spring Boot项目)

4.1 引入依赖

<!– pom.xml –>
<dependency>
   <groupId>com.xuxueli</groupId>
   <artifactId>xxl-job-core</artifactId>
   <version>2.4.0</version>
</dependency>

4.2 配置文件

# application.yml
server:
port: 8081

xxl:
job:
  admin:
    addresses: http://127.0.0.1:8080/xxl-job-admin  # 调度中心地址
  executor:
    appname: my-executor                            # 执行器名称
    port: 9999                                      # 执行器端口(0则自动)
    logpath: /data/applogs/xxl-job/jobhandler        # 日志路径
    logretentiondays: 30                            # 日志保留天数
  accessToken: xxl-job-token                        # 与调度中心一致

4.3 执行器配置类

/**
* XXL-JOB执行器配置
*/
@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.port}")
   private int port;

   @Value("${xxl.job.executor.logpath}")
   private String logPath;

   @Value("${xxl.job.executor.logretentiondays}")
   private int logRetentionDays;

   @Bean
   public XxlJobSpringExecutor xxlJobExecutor() {
       XxlJobSpringExecutor executor = new XxlJobSpringExecutor();
       executor.setAdminAddresses(adminAddresses);
       executor.setAppname(appname);
       executor.setPort(port);
       executor.setLogPath(logPath);
       executor.setLogRetentionDays(logRetentionDays);
       executor.setAccessToken(accessToken);
       return executor;
  }
}


五、任务开发实战

5.1 方式一:Bean模式(推荐)

/**
* 任务Handler:Bean模式
* 在调度中心配置JobHandler名称即可
*/
@Component
public class SampleJobHandler {

   private static final Logger log = LoggerFactory.getLogger(SampleJobHandler.class);

   /**
    * 简单任务示例
    * 调度中心JobHandler填写:sampleJobHandler
    */
   @XxlJob("sampleJobHandler")
   public void sampleJobHandler() throws Exception {
       log.info("XXL-JOB任务执行:sampleJobHandler");
       
       // 业务逻辑
       for (int i = 0; i < 5; i++) {
           log.info("执行进度:{}/5", i + 1);
           Thread.sleep(1000);
      }
       
       log.info("任务执行完成");
  }

   /**
    * 带参数的任务
    * 调度中心运行参数填写:hello world
    */
   @XxlJob("paramJobHandler")
   public void paramJobHandler() throws Exception {
       // 获取调度中心传入的参数
       String param = XxlJobHelper.getJobParam();
       log.info("接收到参数:{}", param);
       
       // 业务处理
       XxlJobHelper.log("参数处理完成:" + param);
  }

   /**
    * 分片任务(大数据量并行处理)
    * 路由策略使用:分片广播
    */
   @XxlJob("shardingJobHandler")
   public void shardingJobHandler() throws Exception {
       // 获取分片参数
       int shardIndex = XxlJobHelper.getShardIndex();  // 当前分片序号
       int shardTotal = XxlJobHelper.getShardTotal();   // 分片总数
       
       log.info("分片参数:当前第{}片,共{}片", shardIndex, shardTotal);
       
       // 业务逻辑:按分片处理数据
       // 例如:shardTotal=3,则每个执行器处理1/3的数据
       List<Integer> dataList = getAllData();
       for (int i = 0; i < dataList.size(); i++) {
           if (i % shardTotal == shardIndex) {
               // 当前执行器负责的数据
               processData(dataList.get(i));
          }
      }
  }
   
   /**
    * 返回执行结果(成功/失败)
    */
   @XxlJob("resultJobHandler")
   public void resultJobHandler() throws Exception {
       try {
           // 业务逻辑
           boolean success = doBusiness();
           
           if (success) {
               XxlJobHelper.handleSuccess("执行成功");
          } else {
               XxlJobHelper.handleFail("执行失败:业务处理异常");
          }
      } catch (Exception e) {
           XxlJobHelper.handleFail("执行异常:" + e.getMessage());
      }
  }
}

5.2 方式二:GLUE模式(Java代码在线编辑)

适用场景:任务逻辑需要频繁修改,不想重新发布服务

操作步骤:
1. 调度中心创建任务,路由策略选择"第一个"或"轮询"
2. 运行模式选择"GLUE(Java)"
3. 点击"GLUE IDE"在线编写代码
4. 保存后立即生效,无需重启
// GLUE模式代码示例(在线编辑)
package com.xxl.job.service.handler;

import com.xxl.job.core.handler.annotation.XxlJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DemoGlueJobHandler {
   
   private static final Logger log = LoggerFactory.getLogger(DemoGlueJobHandler.class);

   @XxlJob("demoGlueJobHandler")
   public void execute() throws Exception {
       log.info("GLUE模式任务执行,代码可在线修改");
       // 业务逻辑…
  }
}


六、调度中心配置详解

6.1 创建任务

1. 打开调度中心:http://localhost:8080
2. 任务管理 → 新增

必填配置:
├── 任务描述:用户数据同步任务
├── 执行器:my-executor(选择刚注册的执行器)
├── JobHandler:sampleJobHandler(与@XxlJob注解值一致)
├── Cron:0 0 2 * * ? (每天凌晨2点执行)
└── 路由策略:轮询

6.2 路由策略说明

策略说明适用场景
第一个 固定选择第一个执行器 单机执行
轮询 依次轮流执行 负载均衡
随机 随机选择执行器 负载均衡
一致性HASH 相同任务始终到同一执行器 有状态任务
最不经常使用 选择使用最少的执行器 动态负载
最近最久未使用 选择最久未使用的执行器 动态负载
故障转移 忽略超时的,选择下一个 高可用
忙碌转移 忽略执行中的,选择下一个 高可用
分片广播 广播到所有执行器,分片执行 大数据量

6.3 Cron表达式速查

格式:秒 分 时 日 月 周

常用示例:
┌──────────────────────────────────────────────────────────┐
│ 0 0/5 * * * ?         每5分钟执行                       │
│ 0 0 2 * * ?           每天凌晨2点                       │
│ 0 0 1 * * 1           每周一凌晨1点                     │
│ 0 0 0 1 * ?           每月1号零点                       │
│ 0 0 0 * * ?           每天零点                          │
│ 0 30 9 * * ?           每天9:30                          │
│ 0 0 9-18 * * ?         每天9-18点整点                    │
│ 0 0/30 9-18 * * ?     每天9-18点,每30分钟              │
└──────────────────────────────────────────────────────────┘


七、实战场景示例

7.1 场景一:数据同步任务

/**
* 每天凌晨同步订单数据
*/
@Component
public class OrderSyncHandler {

   @Autowired
   private OrderService orderService;

   @XxlJob("orderSyncJob")
   public void orderSyncJob() throws Exception {
       // 获取参数:同步天数
       String param = XxlJobHelper.getJobParam();
       int days = param != null ? Integer.parseInt(param) : 1;
       
       // 计算日期范围
       LocalDate endDate = LocalDate.now();
       LocalDate startDate = endDate.minusDays(days);
       
       XxlJobHelper.log("开始同步订单:{} 到 {}", startDate, endDate);
       
       // 分页查询并同步
       int page = 1;
       int pageSize = 1000;
       int total = 0;
       
       while (true) {
           List<Order> orders = orderService.queryByPage(startDate, endDate, page, pageSize);
           if (orders.isEmpty()) break;
           
           // 批量处理
           orderService.batchSync(orders);
           total += orders.size();
           
           XxlJobHelper.log("已同步:{}条", total);
           page++;
      }
       
       XxlJobHelper.handleSuccess("同步完成,共" + total + "条");
  }
}

7.2 场景二:超时订单处理

/**
* 每5分钟检查超时未支付订单
*/
@Component
public class TimeoutOrderHandler {

   @Autowired
   private OrderService orderService;

   @XxlJob("timeoutOrderJob")
   public void timeoutOrderJob() throws Exception {
       // 查询30分钟内未支付的订单
       LocalDateTime timeout = LocalDateTime.now().minusMinutes(30);
       List<Order> timeoutOrders = orderService.queryUnpaidBefore(timeout);
       
       XxlJobHelper.log("发现超时订单:{}笔", timeoutOrders.size());
       
       int successCount = 0;
       for (Order order : timeoutOrders) {
           try {
               orderService.cancelOrder(order.getId());
               successCount++;
          } catch (Exception e) {
               XxlJobHelper.log("取消订单失败:{},原因:{}", order.getId(), e.getMessage());
          }
      }
       
       XxlJobHelper.handleSuccess("处理完成:成功" + successCount + "笔");
  }
}

7.3 场景三:报表生成

/**
* 每天生成日报表(分片广播模式)
*/
@Component
public class ReportHandler {

   @XxlJob("dailyReportJob")
   public void dailyReportJob() throws Exception {
       int shardIndex = XxlJobHelper.getShardIndex();
       int shardTotal = XxlJobHelper.getShardTotal();
       
       XxlJobHelper.log("分片 {}/{} 开始生成报表", shardIndex, shardTotal);
       
       // 按分片处理不同业务线
       List<String> businessLines = Arrays.asList("order", "user", "product", "finance");
       
       for (int i = 0; i < businessLines.size(); i++) {
           if (i % shardTotal == shardIndex) {
               String line = businessLines.get(i);
               XxlJobHelper.log("处理业务线:{}", line);
               generateReport(line);
          }
      }
       
       XxlJobHelper.handleSuccess("报表生成完成");
  }
   
   private void generateReport(String businessLine) {
       // 生成报表逻辑…
  }
}

7.4 场景四:邮件/短信提醒

/**
* 工作日早上9点发送提醒
*/
@Component
public class ReminderHandler {

   @Autowired
   private EmailService emailService;

   @XxlJob("morningReminderJob")
   public void morningReminderJob() throws Exception {
       // 判断是否工作日
       if (!isWorkDay(LocalDate.now())) {
           XxlJobHelper.log("今天不是工作日,跳过");
           return;
      }
       
       // 获取需要提醒的用户
       List<User> users = getReminderUsers();
       
       for (User user : users) {
           try {
               emailService.sendReminder(user.getEmail(), "早安提醒");
               XxlJobHelper.log("发送成功:{}", user.getEmail());
          } catch (Exception e) {
               XxlJobHelper.log("发送失败:{},原因:{}", user.getEmail(), e.getMessage());
          }
      }
  }
   
   private boolean isWorkDay(LocalDate date) {
       DayOfWeek day = date.getDayOfWeek();
       return day != DayOfWeek.SATURDAY && day != DayOfWeek.SUNDAY;
  }
}


八、高级功能

8.1 任务依赖(DAG)

场景:任务A完成后,触发任务B和C,B和C都完成后触发D

配置方式:
1. 任务B、C的"父任务ID"填写A的ID
2. 任务D的"父任务ID"填写B和C的ID

效果:
  A
  / \\
B   C
  \\ /
  D

8.2 邮件报警配置

# 调度中心 application.properties
spring.mail.host=smtp.qq.com
spring.mail.port=465
spring.mail.username=your-email@qq.com
spring.mail.password=your-password
spring.mail.properties.mail.smtp.ssl.enable=true

# 任务失败时发送邮件到:
xxl.job.alarm.email=admin@example.com

8.3 运行报表

调度中心 → 运行报表

可查看:
├── 调度次数趋势
├── 执行结果分布(成功/失败/失败重启)
├── 执行器在线状态
└── 任务执行耗时统计


九、常见问题排查

问题1:执行器注册失败

# 检查项:
1. 调度中心地址是否正确
2. TOKEN是否与调度中心一致
3. 执行器名称(appname)是否在调度中心创建
4. 网络是否通畅

# 日志查看:
tail -f /data/applogs/xxl-job/jobhandler/xxl-job-executor.log

问题2:任务一直不执行

# 检查项:
1. 任务状态是否为"运行中"
2. Cron表达式是否正确(在线工具:cron.qqe2.com)
3. 执行器是否在线
4. 路由策略是否匹配

问题3:任务执行超时

# 解决方案:
1. 增大任务超时时间(默认30分钟)
2. 任务内记录日志:XxlJobHelper.log()
3. 大任务改为分片广播模式


十、面试高频问题

Q1:XXL-JOB的架构是怎样的?

答: 采用中心化架构,由调度中心和执行器两部分组成:

  • 调度中心:负责任务管理、触发调度、日志管理

  • 执行器:注册到调度中心,接收调度请求执行任务

调度中心通过HTTP调用执行器,执行器回调执行结果。

Q2:XXL-JOB如何保证任务不重复执行?

答: 三种机制

  • 路由策略:分片广播模式下,每个执行器只处理自己的分片

  • 分布式锁:调度中心使用数据库锁保证同一任务同一时刻只触发一次

  • 阻塞策略:默认"丢弃后续调度",避免任务堆积

  • Q3:XXL-JOB和Quartz的区别?

    答:

    对比项QuartzXXL-JOB
    架构 嵌入式 中心化
    可视化
    分布式 需自己实现 原生支持
    路由策略 9种策略
    日志管理 完整日志
    报警 邮件报警
    学习成本 中等

    Q4:如何实现任务失败重试?

    答:

    调度中心 → 任务配置 → 失败重试次数:3

    任务代码中抛出异常即为失败:
    if (执行失败) {
    throw new RuntimeException("任务失败");
    }

    调度中心会自动重试指定次数

    Q5:分片广播的使用场景?

    答: 适用于大数据量并行处理

    场景:同步100万条数据

    单机:1个执行器处理100万条,耗时长
    分片:3个执行器各处理33万条,并行执行

    代码获取分片参数:
    int shardIndex = XxlJobHelper.getShardIndex(); // 0,1,2
    int shardTotal = XxlJobHelper.getShardTotal(); // 3


    十一、最佳实践

  • 任务幂等:任务可重复执行,结果一致

  • 日志记录:关键步骤使用XxlJobHelper.log()

  • 超时设置:根据任务耗时合理设置

  • 报警配置:生产环境必须配置邮件报警

  • 分片处理:大数据量任务使用分片广播

  • 参数传递:通过JobParam传递配置,避免硬编码


  • 十二、推荐阅读

    • XXL-JOB官方文档

    • XXL-JOB GitHub

    • Cron表达式在线生成器

    赞(0)
    未经允许不得转载:171主机测评 » XXL-JOB调度中心从入门到实战:分布式任务调度一文搞定
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址