Featured image of post XXL-Job 分布式调度:可视化任务管理 + MySQL 初始化 + 容器化部署

XXL-Job 分布式调度:可视化任务管理 + MySQL 初始化 + 容器化部署

部署 XXL-Job 分布式任务调度平台,初始化 MySQL 数据库表结构,配置执行器注册、任务路由策略、告警通知,docker-compose 完整集成

XXL-Job 是国内最流行的分布式任务调度平台——替代 cron / ScheduledExecutorService 的核心组件。本篇把 Docker 化部署、MySQL 初始化、调度中心 / 执行器接入、告警通知整理清楚。

阅读对象:Java 后端 / 运维工程师 覆盖范围:XXL-Job 调度中心部署 + MySQL 初始化表结构 + 执行器注册 + 任务路由策略(轮询 / 故障转移 / 分片广播)+ 告警邮件 + docker-compose 完整集成

一、为什么需要分布式调度

传统定时任务的痛点:

  • 单机 cron:服务器挂了任务就没了
  • ScheduledExecutorService:分布式下会重复执行(3 台机器都跑)
  • 手动控制:凌晨 3 点被叫起来手动跑批

XXL-Job 的解决方案

能力说明
可视化管理Web 端配置任务、查看日志、触发重跑
分布式执行任务在多台执行器中只跑一次(通过路由策略)
任务分片1 个任务广播到 10 台机器并行处理(10x 提速)
失败重试自动重试 N 次,指数退避
告警通知任务失败邮件 / 钉钉 / 飞书通知
GLUE 模式Web 端直接写 Java / Shell / Python 脚本
任务依赖父任务完成触发子任务(DAG

When to use XXL-Job

  • 替代 crontab(跨服务器去重 + 可视化
  • 跑批任务(日终结算、数据归档)
  • 第三方接口同步(每 5 分钟拉一次数据)
  • 定时清理(30 天前的日志)
  • 订单超时关闭(每分钟扫描一次)

二、架构原理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
┌──────────────────┐                ┌──────────────────┐
│  调度中心 (Admin) │◄──────────────►│  执行器 (Executor)│
│   - 任务配置      │     注册       │   - 业务服务       │
│   - 触发调度      │                │   - 接收任务       │
│   - 日志收集      │                │   - 执行回调       │
│   - 告警通知      │                │                   │
└────────┬─────────┘                └──────────────────┘
   ┌──────────┐
   │  MySQL   │
   │ (元数据) │
   └──────────┘
  • 调度中心(Admin):独立部署,Web 管理界面
  • 执行器(Executor):嵌入到业务服务里,通过 jar 包依赖
  • MySQL:存任务配置 / 执行日志 / 调度锁

核心协议:执行器启动时主动注册到调度中心;调度中心触发后通过 RPC 调执行器。

三、MySQL 初始化

3.1 下载 SQL 脚本

1
2
# XXL-Job 2.4.0 表结构
wget https://github.com/xuxueli/xxl-job/blob/2.4.0/doc/db/tables_xxl_job.sql

完整表结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
CREATE DATABASE IF NOT EXISTS `xxl_job` DEFAULT CHARSET utf8mb4;

USE `xxl_job`;

SET NAMES utf8mb4;

CREATE TABLE `xxl_job_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `job_group` int(11) NOT NULL COMMENT '执行器主键ID',
  `job_desc` varchar(255) NOT NULL,
  `add_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `author` varchar(64) DEFAULT NULL,
  `alarm_email` varchar(255) DEFAULT NULL,
  `schedule_type` varchar(50) NOT NULL DEFAULT 'NONE' COMMENT '调度类型',
  `schedule_conf` varchar(128) DEFAULT NULL COMMENT '调度配置',
  `misfire_strategy` varchar(50) NOT NULL DEFAULT 'DO_NOTHING',
  `executor_route_strategy` varchar(50) DEFAULT NULL,
  `executor_handler` varchar(255) DEFAULT NULL,
  `executor_param` varchar(512) DEFAULT NULL,
  `executor_block_strategy` varchar(50) DEFAULT NULL,
  `executor_timeout` int(11) NOT NULL DEFAULT '0',
  `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0',
  `glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型',
  `glue_source` mediumtext COMMENT 'GLUE源代码',
  `glue_remark` varchar(128) DEFAULT NULL,
  `glue_updatetime` datetime DEFAULT NULL,
  `child_jobid` varchar(255) DEFAULT NULL,
  `trigger_status` tinyint(4) NOT NULL DEFAULT '0',
  `trigger_last_time` bigint(13) NOT NULL DEFAULT '0',
  `trigger_next_time` bigint(13) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 还有:xxl_job_log, xxl_job_log_report, xxl_job_logglue, xxl_job_registry,
--      xxl_job_group, xxl_job_user, xxl_job_lock

8 张表

表名作用
xxl_job_info任务配置
xxl_job_log任务执行日志
xxl_job_log_report日志报表
xxl_job_logglueGLUE 源码历史
xxl_job_registry执行器注册表
xxl_job_group执行器分组
xxl_job_user调度中心用户
xxl_job_lock分布式锁

3.2 初始化用户

执行完 SQL 后,默认账号admin / 123456生产必改!

四、调度中心部署

4.1 拉取镜像

1
docker pull xuxueli/xxl-job-admin:2.4.0

4.2 启动容器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
docker run -d --restart=always --name xxl-job-admin \
  --net=host \
  -v /home/docker/xxl-job/data:/data \
  -e PARAMS=" \
    --spring.datasource.url=jdbc:mysql://10.0.1.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai \
    --spring.datasource.username=root \
    --spring.datasource.password={{DB_PASS}}" \
  -e JAVA_OPTS="-Xms2g -Xmx2g -Xmn1g -Xss1m \
                -XX:SurvivorRatio=8 \
                -XX:MetaspaceSize=256m \
                -XX:MaxMetaspaceSize=256m \
                -XX:NativeMemoryTracking=detail" \
  xuxueli/xxl-job-admin:2.4.0

关键参数

  • --net=host:管理端口 8080 直接暴露
  • PARAMS:MySQL 连接信息
  • JAVA_OPTS:JVM 调优
  • 数据卷 /data 持久化日志

4.3 访问调度中心

1
2
http://<IP>:8080/xxl-job-admin
默认账号:admin / 123456

4.4 docker-compose 完整版

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
version: "3"
services:
  mysql:
    image: mysql:8.0
    container_name: xxl-job-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: {{DB_ROOT_PASS}}
    volumes:
      - /home/xxl-job/mysql:/var/lib/mysql
    command: --default-authentication-plugin=mysql_native_password

  xxl-job-admin:
    image: xuxueli/xxl-job-admin:2.4.0
    container_name: xxl-job-admin
    restart: always
    depends_on:
      - mysql
    ports:
      - "8080:8080"
    volumes:
      - /home/xxl-job/logs:/data/applogs
    environment:
      PARAMS: >
        --spring.datasource.url=jdbc:mysql://mysql:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8
        --spring.datasource.username=root
        --spring.datasource.password={{DB_ROOT_PASS}}
      JAVA_OPTS: "-Xms1g -Xmx1g"

首次启动

  1. 启动 MySQL 容器
  2. 手动导入 tables_xxl_job.sql
  3. 启动 xxl-job-admin

五、执行器接入(业务服务)

5.1 添加依赖

1
2
3
4
5
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>2.4.0</version>
</dependency>

5.2 配置 application.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
xxl:
  job:
    admin:
      addresses: http://xxl-job-admin.internal.example.com:8080/xxl-job-admin
    executor:
      appname: my-service
      address:
      ip:
      port: 9999
      logpath: /app/logs/xxl-job/jobhandler
      logretentiondays: 30
    accessToken: {{XXL_JOB_TOKEN}}

5.3 写任务 Handler

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Component
@JobHandler(value = "demoJobHandler")
public class DemoJobHandler extends IJobHandler {

    @Override
    public ReturnT<String> execute(String param) throws Exception {
        // 业务逻辑
        log.info("XXL-JOB Hello, param: {}", param);
        return ReturnT.SUCCESS;
    }
}

或者注解方式

1
2
3
4
5
@XxlJob("myJobHandler")
public ReturnT<String> myJobHandler() throws Exception {
    // 业务逻辑
    return ReturnT.SUCCESS;
}

5.4 启动服务

业务服务启动时自动注册到调度中心,30 秒内在 Admin 后台 → 执行器管理 看到

六、任务配置

6.1 创建执行器

Admin → 执行器管理新增

1
2
3
4
AppName:my-service
名称:我的服务
注册方式:自动注册
机器地址:(自动填入)

6.2 创建任务

Admin → 任务管理新增

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
执行器:my-service
任务描述:每日订单结算
调度类型:CRON
Cron:0 0 2 * * ?              # 每天凌晨 2 点
运行模式:BEAN
JobHandler:demoJobHandler
路由策略:第一个
阻塞处理策略:单机串行
失败重试:3 次
告警邮件:ops@example.com

6.3 路由策略详解

策略行为适用
第一个固定选第一台任务不能并行
轮询1→2→3→1→2→3负载均衡
随机随机选一台简单场景
一致性 HASH同参数同机器分片场景
最不经常使用选使用最少的性能均衡
故障转移失败后切下一台高可用
忙碌转移跳过忙碌选空闲实时任务
分片广播所有机器都跑并行处理

6.4 分片广播实战

场景:10 万条订单待结算,1 台机器跑要 1 小时

配置

1
路由策略:分片广播

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@XxlJob("settleOrdersJobHandler")
public ReturnT<String> settleOrdersJobHandler() throws Exception {
    // 分片参数
    int shardIndex = XxlJobHelper.getShardIndex();   // 当前分片(0 ~ shardTotal-1)
    int shardTotal = XxlJobHelper.getShardTotal();   // 总分片数

    // 假设有 10 万条待结算订单
    int total = 100000;
    int pageSize = total / shardTotal + 1;
    int from = shardIndex * pageSize;
    int to = Math.min(from + pageSize, total);

    // 处理 [from, to) 范围的订单
    List<Order> orders = orderDao.findPending(from, to);
    for (Order order : orders) {
        settle(order);
    }

    return ReturnT.SUCCESS;
}

效果

  • 5 台执行器 → 每台处理 2 万条 → 5x 提速

七、GLUE 模式

Web 端直接写代码——不用改业务服务:

7.1 GLUE Java

任务管理 → 选任务 → GLUE IDE → 写 Java:

1
2
3
4
5
6
7
8
public class GlueJobHandler extends IJobHandler {
    @Override
    public ReturnT<String> execute(String param) throws Exception {
        // 直接写业务逻辑
        // 注意:只能用 JDK 内置类,第三方类用不了
        return ReturnT.SUCCESS;
    }
}

适用:临时脚本(数据修复、临时跑批)。

7.2 GLUE Shell

1
2
3
#!/bin/bash
echo "xxl-job: hello shell"
echo "参数:$1"

7.3 GLUE Python

1
2
3
4
5
6
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os
import sys
print("xxl-job: hello python")
print("参数:", sys.argv[1])

7.4 GLUE PHP / Node.js

类似,不展开。

八、告警通知

8.1 邮件告警

任务配置

1
告警邮件:ops@example.com

Admin application.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
spring:
  mail:
    host: smtp.example.com
    port: 465
    username: noreply@example.com
    password: {{SMTP_PASS}}
xxl:
  job:
    triggerpool:
      fast:
        max: 200

8.2 钉钉告警

自定义告警实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Component
public class DingTalkJobAlarm implements JobAlarm {
    @Override
    public boolean doAlarm(XxlJobInfo info, XxlJobLog log) {
        // 调钉钉 Webhook
        String text = String.format("任务 [%s] 执行失败\n日志:%s", 
                                     info.getJobDesc(),
                                     log.getHandleMsg());
        DingTalk.send("{{DINGTALK_TOKEN}}", text);
        return true;
    }
}

注册

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Configuration
public class XxlJobConfig {
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        XxlJobSpringExecutor executor = new XxlJobSpringExecutor();
        executor.setAdminAddresses("http://xxl-job-admin:8080/xxl-job-admin");
        executor.setAppname("my-service");
        executor.setJobAlarm(new DingTalkJobAlarm());   // 自定义告警
        return executor;
    }
}

九、监控与日志

9.1 任务执行日志

任务管理 → 选任务 → 操作 → 日志 → 看每次执行:

  • 触发时间
  • 执行结果(成功 / 失败 / 调度失败)
  • 耗时
  • 失败原因(堆栈)
  • 重试次数

日志保留xxl.job.executor.logretentiondays=30(默认 30 天)。

9.2 调度报表

Admin → 调度报表

1
2
成功 / 失败 / 调度失败 / 总数
按日 / 周 / 月 聚合

实战用法:每周一看上周的报表,任务成功率 < 99% 就要查问题

9.3 告警指标

接入 Prometheus(自定义 exporter):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Aspect
@Component
public class XxlJobMetricsAspect {
    @Around("@annotation(com.xxl.job.core.handler.annotation.XxlJob)")
    public void track(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            pjp.proceed();
            Metrics.counter("xxl_job_success", "name", pjp.getSignature().getName()).increment();
        } catch (Exception e) {
            Metrics.counter("xxl_job_failure", "name", pjp.getSignature().getName()).increment();
        } finally {
            Metrics.timer("xxl_job_duration", "name", pjp.getSignature().getName())
                  .record(System.currentTimeMillis() - start, TimeUnit.MILLISECONDS);
        }
    }
}

十、典型坑位

10.1 任务执行但 Admin 看不到日志

症状:执行器跑完了任务,Admin 端日志查询不到

原因:执行器 xxl.job.executor.logpath 路径配置错误。

修复

1
2
3
4
xxl:
  job:
    executor:
      logpath: /data/applogs/xxl-job/jobhandler    # 必须是绝对路径

容器内注意:路径要在挂载卷内(否则容器重启日志丢)。

10.2 任务调度延迟

症状:任务应该 2 点跑,实际 2:05 才跑

原因

  • 调度中心集群只 1 台,单点压力
  • 任务太多(> 1000 个),调度中心处理不过来

修复

  • 调度中心集群部署
  • 减少短间隔任务(每分钟 1 次的太多)
  • 加大 xxl.job.triggerpool.fast.max

10.3 任务重复执行

症状:1 个任务在 2 台机器都跑了。

原因:路由策略选错(**不能用"分片广播"**用于只能跑 1 次的任务)。

修复:选 故障转移第一个默认)。

10.4 GLUE 模式不能用第三方类

修复

1
2
GLUE Java 只能用 JDK 内置类
要用 Spring Bean,注解 @XxlJob

十一、生产部署清单

11.1 调度中心高可用

两台调度中心集群

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 调度中心 1
xxl-job-admin:
  ports: 8080
  environment:
    PARAMS: "--xxl.job.accessToken={{TOKEN}}"

# 调度中心 2
xxl-job-admin:
  ports: 8080
  environment:
    PARAMS: "--xxl.job.accessToken={{TOKEN}}"

执行器配置 2 个地址

1
2
3
4
xxl:
  job:
    admin:
      addresses: http://admin1:8080/xxl-job-admin,http://admin2:8080/xxl-job-admin

任务触发时会轮询选一个调度中心。

11.2 数据库高可用

1
2
3
4
5
6
# MySQL 主从 + 读写分离
spring:
  datasource:
    url: jdbc:mysql://mysql-primary:3306/xxl_job
    # 读流量大的话可加:
    # url: jdbc:mysql:loadbalance://mysql-primary:3306,mysql-replica:3306/xxl_job

11.3 监控

  • Prometheus 抓 Admin metrics
  • 告警规则
    • 任务失败率 > 5% → 告警
    • 调度延迟 > 5 分钟 → 告警
    • 调度中心内存 > 80% → 告警

十二、完整 docker-compose

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
version: "3.8"
services:
  mysql:
    image: mysql:8.0
    container_name: xxl-job-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: {{DB_ROOT_PASS}}
    volumes:
      - /home/xxl-job/mysql:/var/lib/mysql
    networks:
      - xxl-net

  xxl-job-admin:
    image: xuxueli/xxl-job-admin:2.4.0
    container_name: xxl-job-admin
    restart: always
    depends_on:
      - mysql
    ports:
      - "8080:8080"
    volumes:
      - /home/xxl-job/logs:/data/applogs
    environment:
      PARAMS: >
        --spring.datasource.url=jdbc:mysql://mysql:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
        --spring.datasource.username=root
        --spring.datasource.password={{DB_ROOT_PASS}}
        --xxl.job.accessToken={{XXL_JOB_TOKEN}}
      JAVA_OPTS: "-Xms1g -Xmx1g -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
    networks:
      - xxl-net

networks:
  xxl-net:
    driver: bridge

十三、最佳实践清单

  • MySQL 8.0 + utf8mb4——中文支持好
  • 调度中心至少 2 台集群——避免单点
  • 路由策略根据场景选
    • 跑批任务 → 故障转移(高可用)
    • 同步任务 → 分片广播(并行)
    • 普通定时 → 第一个(避免重复)
  • 告警邮件必开——失败 5 分钟内收到
  • 任务日志保留 30 天——logretentiondays=30
  • 失败重试 3 次——临时网络抖动不报错
  • 执行器 ip+port 唯一——xxl.job.executor.ip 显式指定
  • GLUE 用于临时脚本——生产任务用 @XxlJob 注解
  • JVM 调优UseContainerSupport + MaxRAMPercentage
  • 任务配置备份:MySQL 定期 dump

2024+ 视角补充

本文写于 2023-11,2024-2026 期间 XXL-Job / 分布式调度生态关键演进:

  • XXL-Job 2.4+ → 2.5+(2024-2025):Spring Boot 3.x 兼容JDK 17 强制K8s 原生调度(不再依赖固定 IP)
  • XXL-Job 2.5+内置 OpenTelemetry 集成Web IDE 增强(GLUE Java 支持 Spring Bean)
  • PowerJob 5.x(2024-2025):K8s 原生 Worker(动态注册);DAG 工作流 2.0MapReduce 性能 10x——vs XXL-JOB 更现代
  • DolphinScheduler 3.2+(2024-2026):Apache 顶级项目大数据集成(Spark / Flink / Hive);K8s 调度原生多租户 + 工作流 3.0
  • Airflow 2.10+(2024-2026):Python 系首选TaskFlow API(Pythonic DAG);Kubernetes Executor 2.0
  • Temporal 1.25+(2024-2026):Workflow-as-Code 之王——状态机 + 长期运行任务首选
  • KubeVela 1.10+云原生应用编排——微服务编排 + 灰度发布
  • Serverless CronAWS EventBridge Scheduler / 阿里云定时任务 / GCP Cloud Scheduler 2024+ 仍是云端首选

实战建议(2025-2026 视角)

  • Java 业务定时任务XXL-Job 2.5+(轻量、上手快)或 PowerJob 5.x(更现代)
  • 大数据 / 离线数仓DolphinScheduler 3.2+(Apache 顶级项目)
  • Python 系Airflow 2.10+
  • 复杂工作流 / 长期任务Temporal 1.25+2024-2026 趋势
  • Serverless / 简单定时 → 云厂商 EventBridge / 定时任务

下一步

使用 Hugo 构建
主题 StackJimmy 设计