Skip to content

XXL-JOB

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

核心特性

任务调度

  • 定时任务 - 支持Cron表达式定时调度
  • 分片任务 - 支持任务分片并行执行
  • 故障转移 - 任务执行失败自动转移到其他节点
  • 动态分组 - 执行器动态注册和分组管理

执行策略

  • BEAN模式 - 基于Spring Bean的任务执行
  • GLUE模式 - 支持在线编辑任务代码
  • 命令行模式 - 执行shell命令和脚本
  • HTTP模式 - 通过HTTP接口调用任务

高可用性

  • 调度中心集群 - 支持调度中心集群部署
  • 执行器集群 - 支持执行器集群部署
  • 失败处理策略 - 失败告警、失败重试等策略
  • 监控报警 - 任务执行状态监控和邮件告警

架构设计

核心组件

XXL-JOB架构:
├── 调度中心 (Admin)
│   ├── 任务管理
│   ├── 调度管理  
│   ├── 执行器管理
│   └── 日志管理
└── 执行器 (Executor)
    ├── 任务注册
    ├── 任务执行
    ├── 日志回传
    └── 心跳检测

通信机制

  • 调度通信 - 调度中心通过HTTP调用执行器
  • 注册发现 - 执行器自动注册到调度中心
  • 心跳检测 - 定期心跳保持连接
  • 日志回传 - 执行日志实时回传到调度中心

快速开始

调度中心部署

数据库初始化

sql
-- 执行建表脚本 /xxl-job/doc/db/tables_xxl_job.sql

-- 关键表结构
xxl_job_info          -- 任务信息表
xxl_job_log           -- 任务日志表  
xxl_job_log_report    -- 日志报表
xxl_job_logglue       -- GLUE代码
xxl_job_registry      -- 执行器注册表
xxl_job_group         -- 执行器组
xxl_job_user          -- 用户表

配置文件

点击查看完整代码实现
properties
# 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=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# 调度中心配置
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
xxl.job.admin.username=admin
xxl.job.admin.password=123456

# 邮件告警配置
spring.mail.host=smtp.qq.com
spring.mail.port=25
spring.mail.username=xxx@qq.com
spring.mail.password=xxx
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true

执行器项目集成

Maven依赖

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

配置类

点击查看完整代码实现
java
@Configuration
public class XxlJobConfig {
    
    @Value("${xxl.job.admin.addresses}")
    private String adminAddresses;
    
    @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.accessToken}")
    private String accessToken;
    
    @Value("${xxl.job.executor.logpath}")
    private String logPath;
    
    @Value("${xxl.job.executor.logretentiondays}")
    private int logRetentionDays;
    
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        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;
    }
}

应用配置

properties
# xxl-job executor配置
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
xxl.job.executor.appname=xxl-job-executor-sample
xxl.job.executor.address=
xxl.job.executor.ip=
xxl.job.executor.port=9999
xxl.job.accessToken=
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
xxl.job.executor.logretentiondays=30

任务开发

BEAN模式任务

简单任务

java
@Component
public class SimpleJobHandler {
    
    @XxlJob("simpleJobHandler")
    public ReturnT<String> simpleJobHandler(String param) {
        XxlJobLogger.log("XXL-JOB Hello World.");
        return ReturnT.SUCCESS;
    }
}

分片任务

点击查看完整代码实现
java
@Component
public class ShardingJobHandler {
    
    @XxlJob("shardingJobHandler")
    public ReturnT<String> shardingJobHandler(String param) {
        
        // 分片参数
        int shardIndex = XxlJobContext.getXxlJobContext().getShardIndex();
        int shardTotal = XxlJobContext.getXxlJobContext().getShardTotal();
        
        XxlJobLogger.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal);
        
        // 业务逻辑
        for (int i = 0; i < 10000; i++) {
            if (i % shardTotal == shardIndex) {
                XxlJobLogger.log("第 {} 个任务,由分片 {} 执行", i, shardIndex);
                // 处理业务逻辑
            }
        }
        
        return ReturnT.SUCCESS;
    }
}

生命周期管理

点击查看完整代码实现
java
@Component
public class LifecycleJobHandler {
    
    @XxlJob("lifecycleJobHandler")
    public ReturnT<String> lifecycleJobHandler(String param) {
        
        XxlJobLogger.log("任务开始执行, 参数: {}", param);
        
        try {
            // 业务逻辑
            processBusinessLogic(param);
            
            XxlJobLogger.log("任务执行成功");
            return ReturnT.SUCCESS;
            
        } catch (Exception e) {
            XxlJobLogger.log("任务执行异常: {}", e.getMessage());
            return new ReturnT<String>(ReturnT.FAIL_CODE, "任务执行失败: " + e.getMessage());
        }
    }
    
    private void processBusinessLogic(String param) {
        // 检查任务是否被中断
        if (XxlJobContext.getXxlJobContext().getJobInterrupt()) {
            XxlJobLogger.log("任务被中断");
            return;
        }
        
        // 业务处理逻辑
        for (int i = 0; i < 100; i++) {
            if (XxlJobContext.getXxlJobContext().getJobInterrupt()) {
                break;
            }
            // 处理第i个任务
            XxlJobLogger.log("处理第{}个任务", i);
        }
    }
}

GLUE模式任务

Java代码任务

java
// 在调度中心GLUE IDE中编写
public class GlueJobHandler extends IJobHandler {
    
    @Override
    public ReturnT<String> execute(String param) throws Exception {
        XxlJobLogger.log("XXL-JOB, Hello World.");
        
        // 可以访问Spring容器中的Bean
        ApplicationContext applicationContext = XxlJobContext.getXxlJobContext().getApplicationContext();
        Object bean = applicationContext.getBean("userService");
        
        return ReturnT.SUCCESS;
    }
}

Shell脚本任务

点击查看完整代码实现
bash
#!/bin/bash
echo "XXL-JOB Shell Job Start"

# 获取任务参数
param=$1
echo "Job Param: $param"

# 执行业务逻辑
if [ -f "/tmp/test.txt" ]; then
    echo "File exists"
else
    echo "File not exists, creating..."
    touch /tmp/test.txt
fi

echo "XXL-JOB Shell Job End"
exit 0

Python脚本任务

点击查看完整代码实现
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import datetime

def main():
    print("XXL-JOB Python Job Start")
    
    # 获取任务参数
    param = sys.argv[1] if len(sys.argv) > 1 else ""
    print(f"Job Param: {param}")
    
    # 执行业务逻辑
    current_time = datetime.datetime.now()
    print(f"Current time: {current_time}")
    
    print("XXL-JOB Python Job End")
    return 0

if __name__ == "__main__":
    sys.exit(main())

高级功能

任务依赖

点击查看完整代码实现
java
// 子任务处理器
@Component
public class ChildJobHandler {
    
    @XxlJob("childJobHandler")
    public ReturnT<String> childJobHandler(String param) {
        XxlJobLogger.log("子任务执行, 参数: {}", param);
        
        // 触发子任务
        List<Long> jobIds = Arrays.asList(2L, 3L, 4L);
        ReturnT<String> result = XxlJobContext.getXxlJobContext().triggerChildJob(jobIds, "子任务参数");
        
        return result;
    }
}

任务调度API

点击查看完整代码实现
java
@RestController
public class JobApiController {
    
    @Autowired
    private JobApiService jobApiService;
    
    // 手动触发任务
    @PostMapping("/job/trigger")
    public String triggerJob(@RequestParam("jobId") int jobId,
                           @RequestParam("executorParam") String executorParam) {
        return jobApiService.trigger(jobId, executorParam, null);
    }
    
    // 新增任务
    @PostMapping("/job/add")
    public String addJob(@RequestBody XxlJobInfo jobInfo) {
        return jobApiService.add(jobInfo);
    }
    
    // 更新任务
    @PostMapping("/job/update")
    public String updateJob(@RequestBody XxlJobInfo jobInfo) {
        return jobApiService.update(jobInfo);
    }
    
    // 删除任务
    @PostMapping("/job/remove")
    public String removeJob(@RequestParam("id") int id) {
        return jobApiService.remove(id);
    }
    
    // 启动任务
    @PostMapping("/job/start")
    public String startJob(@RequestParam("id") int id) {
        return jobApiService.start(id);
    }
    
    // 停止任务
    @PostMapping("/job/stop")
    public String stopJob(@RequestParam("id") int id) {
        return jobApiService.stop(id);
    }
}

动态任务管理

点击查看完整代码实现
java
@Service
public class DynamicJobService {
    
    @Value("${xxl.job.admin.addresses}")
    private String adminAddresses;
    
    @Value("${xxl.job.admin.username}")
    private String username;
    
    @Value("${xxl.job.admin.password}")
    private String password;
    
    public void createDynamicJob() {
        // 动态创建任务
        XxlJobInfo jobInfo = new XxlJobInfo();
        jobInfo.setJobGroup(1);
        jobInfo.setJobCron("0 0/1 * * * ?");
        jobInfo.setJobDesc("动态创建的任务");
        jobInfo.setAuthor("admin");
        jobInfo.setAlarmEmail("xxx@xxx.com");
        jobInfo.setExecutorRouteStrategy(ExecutorRouteStrategyEnum.ROUND.name());
        jobInfo.setExecutorHandler("dynamicJobHandler");
        jobInfo.setExecutorParam("");
        jobInfo.setExecutorBlockStrategy(ExecutorBlockStrategyEnum.SERIAL_EXECUTION.name());
        jobInfo.setExecutorTimeout(0);
        jobInfo.setExecutorFailRetryCount(0);
        jobInfo.setGlueType(GlueTypeEnum.BEAN.name());
        
        // 调用API创建任务
        String result = XxlJobAdminApi.addJob(adminAddresses, username, password, jobInfo);
        System.out.println("创建任务结果: " + result);
    }
}

监控告警

邮件告警配置

properties
# 邮件配置
spring.mail.host=smtp.163.com
spring.mail.port=25  
spring.mail.username=xxx@163.com
spring.mail.password=xxx
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

自定义告警

点击查看完整代码实现
java
@Component
public class CustomAlarmHandler implements JobAlarm {
    
    @Override
    public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog) {
        
        // 钉钉告警
        sendDingTalkAlarm(info, jobLog);
        
        // 微信告警
        sendWeChatAlarm(info, jobLog);
        
        // 短信告警
        sendSmsAlarm(info, jobLog);
        
        return true;
    }
    
    private void sendDingTalkAlarm(XxlJobInfo info, XxlJobLog jobLog) {
        // 实现钉钉机器人告警
        String message = String.format("任务执行失败\\n任务名称: %s\\n失败时间: %s\\n失败原因: %s", 
            info.getJobDesc(), 
            new Date(jobLog.getTriggerTime()), 
            jobLog.getHandleMsg());
            
        // 发送钉钉消息
        DingTalkUtil.sendMessage(message);
    }
}

监控面板

点击查看完整代码实现
java
// 自定义监控指标
@Component
public class JobMetrics {
    
    private final MeterRegistry meterRegistry;
    private final Counter successCounter;
    private final Counter failCounter;
    private final Timer executionTimer;
    
    public JobMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        this.successCounter = Counter.builder("xxl.job.success")
            .description("成功执行的任务数")
            .register(meterRegistry);
        this.failCounter = Counter.builder("xxl.job.fail")
            .description("失败执行的任务数")
            .register(meterRegistry);
        this.executionTimer = Timer.builder("xxl.job.execution.time")
            .description("任务执行时间")
            .register(meterRegistry);
    }
    
    // 在任务执行时调用
    public void recordSuccess() {
        successCounter.increment();
    }
    
    public void recordFailure() {
        failCounter.increment();
    }
    
    public void recordExecutionTime(Duration duration) {
        executionTimer.record(duration);
    }
}

部署运维

Docker部署

点击查看完整代码实现
yaml
# docker-compose.yml
version: '3'
services:
  xxl-job-admin:
    image: xuxueli/xxl-job-admin:2.4.0
    container_name: xxl-job-admin
    ports:
      - "8080:8080"
    environment:
      PARAMS: '--spring.datasource.url=jdbc:mysql://mysql:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai --spring.datasource.username=root --spring.datasource.password=123456'
    depends_on:
      - mysql
    volumes:
      - ./data/applogs:/data/applogs

  mysql:
    image: mysql:8.0
    container_name: mysql
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: xxl_job
    ports:
      - "3306:3306"
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/init:/docker-entrypoint-initdb.d

集群部署

bash
# 调度中心集群
# 节点1
java -jar xxl-job-admin.jar --server.port=8080

# 节点2  
java -jar xxl-job-admin.jar --server.port=8081

# 执行器配置多个调度中心地址
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin,http://127.0.0.1:8081/xxl-job-admin

最佳实践

任务设计原则

  • 任务保持幂等性,支持重复执行
  • 合理设置任务超时时间
  • 避免任务之间产生循环依赖
  • 大任务拆分成小任务分片执行

性能优化

  • 合理配置线程池大小
  • 避免在高峰期执行重负载任务
  • 使用分片任务提升并发能力
  • 定期清理过期的执行日志

监控运维

  • 建立完善的告警机制
  • 监控任务执行状态和耗时
  • 定期备份任务配置和数据
  • 制定故障应急处理流程

XXL-JOB凭借其简洁的设计理念、丰富的功能特性和良好的扩展性,成为Java生态中最受欢迎的分布式任务调度平台之一,为企业级应用提供了可靠的定时任务解决方案。

正在精进