XxlJob 面试题

3 阅读6分钟

1. 为什么用 XxlJob?

Q:为什么项目中选择使用 XxlJob?相比其他定时任务方案有什么优势?

A: XxlJob 是一个轻量级分布式任务调度平台,选择它的核心原因如下:

对比维度Spring @ScheduledQuartzXxlJob
集群/分布式❌ 不支持⚠️ 需数据库锁,性能差✅ 原生支持
可视化管理❌ 无❌ 无✅ Web 控制台
动态调整❌ 需重启⚠️ 需手动操作DB✅ 页面动态修改
任务分片❌ 不支持⚠️ 不支持✅ 支持
失败重试❌ 不支持⚠️ 简单重试✅ 策略丰富
调度日志❌ 无⚠️ 需自建✅ 完整日志

总结优势:

  • 简单易用:学习成本低,通过注解即可接入
  • 分布式:天然支持集群部署,任务不会重复执行
  • 可视化:提供 Web 控制台,方便管理任务、查看日志
  • 弹性扩容:支持动态增减执行器实例
  • 路由策略丰富:支持多种路由方式(轮询、随机、故障转移等)
  • 完善的基础功能:失败重试、报警、任务分片、GLUE 模式等

2. XxlJob 的具体使用步骤

Q:在 Spring Boot 项目中集成和使用 XxlJob 的完整步骤是什么?

A:

第一步:引入依赖

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

第二步:添加配置

application.yml 中配置:

xxl:
  job:
    admin:
      addresses: http://127.0.0.1:8080/xxl-job-admin  # 调度中心地址
    executor:
      appname: xxl-job-executor-sample                  # 执行器名称
      port: 9999                                         # 执行器端口
      logpath: /data/applogs/xxl-job                     # 日志路径
      logretentiondays: 30                               # 日志保留天数

第三步:编写配置类

@Configuration
public class XxlJobConfig {

    @Value("${xxl.job.admin.addresses}")
    private String adminAddresses;

    @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);
        return executor;
    }
}

第四步:编写任务处理类

@Component
public class SampleXxlJob {

    @XxlJob("demoJobHandler")
    public void demoJobHandler() throws Exception {
        XxlJobHelper.log("XXL-JOB, Hello World.");
        for (int i = 0; i < 5; i++) {
            XxlJobHelper.log("beat at:" + i);
            TimeUnit.SECONDS.sleep(2);
        }
    }
}

第五步:在控制台配置任务

  1. 登录 XxlJob Admin 控制台(默认端口 8080)
  2. 新增执行器(AppName 与配置中一致)
  3. 新增任务,设置 JobHandler 为 demoJobHandler
  4. 选择调度类型(CRON)、路由策略等
  5. 启动任务

3. XxlJob 的路由策略有几种?

Q:XxlJob 提供了哪些路由策略?分别适用于什么场景?

A: XxlJob 提供了以下路由策略:

策略名称说明适用场景
第一个固定选择第一个机器默认策略,简单场景
最后一个固定选择最后一个机器
轮询(ROUND)按顺序依次分配最常用,负载均衡
随机(RANDOM)随机选择负载均衡
一致性哈希(CONSISTENT_HASH)根据 Job 参数哈希到固定机器相同参数始终路由到同一台机器
最不经常使用(LFU)优先选择使用频率最低的机器冷启动任务均衡
最近最久未使用(LRU)优先选择最长时间未使用的机器资源利用率均衡
故障转移(FAILOVER)按照 LR 策略顺序检测,跳过故障机器高可用场景
忙碌转移(BUSYOVER)按照 LR 策略顺序检测,跳过忙碌机器避免任务堆积
分片广播(SHARDING_BROADCAST)广播到所有机器,每台处理不同分片大数据量处理

面试重点 —— 分片广播:

// 获取总分片数和当前分片索引
int shardTotal = XxlJobHelper.getShardTotal();  // 总分片数
int shardIndex = XxlJobHelper.getShardIndex();  // 当前分片序号

// 根据 shardIndex 处理对应范围的数据
// 例如:查数据库时带上条件 id % shardTotal == shardIndex

3 台机器 → shardTotal=3,shardIndex 分别为 0、1、2,各自处理 1/3 的数据。


4. XxlJob 是如何防止任务重复执行的?

Q:在集群环境下,XxlJob 如何确保同一个任务不会被多个节点重复执行?

A: XxlJob 通过以下机制防止任务重复执行:

4.1 基于 MySQL 行锁的调度竞争

调度中心在触发任务时,通过数据库行锁保证只有一个调度线程能触发任务:

-- 核心思路(简化)
UPDATE xxl_job_lock SET lock_time = NOW() WHERE lock_name = 'schedule_lock';
  • 多个 Admin 调度线程竞争同一把锁
  • 抢到锁的线程负责扫描并触发任务
  • 没抢到的线程直接跳过

4.2 路由策略保证单节点执行

对于非分片广播任务,路由策略(轮询、故障转移等)只会选择一个执行器节点去执行,从路由层面避免了重复。

4.3 执行器回调去重

  • 执行器在处理任务时,会根据 jobId 和触发时间判断是否已处理过
  • 已处理过的任务不会重复执行

4.4 分片任务的防重

分片广播场景下:

  • 每个分片节点获取不同的 shardIndex
  • 通过业务逻辑(如 id % shardTotal == shardIndex)保证每条数据只被一个节点处理
  • 天然不存在重复执行问题

5. XxlJob 如何提升任务处理的效率?

Q:XxlJob 有哪些机制来提升任务处理的效率?

A:

5.1 任务分片(核心手段)

将大任务拆分成多个小任务,并行分配给多台机器执行:

@XxlJob("shardingJobHandler")
public void shardingJobHandler() throws Exception {
    int shardTotal = XxlJobHelper.getShardTotal();
    int shardIndex = XxlJobHelper.getShardIndex();

    // 多台机器并行处理各自分片,效率线性提升
    List<Data> dataList = queryData(shardTotal, shardIndex);
    for (Data data : dataList) {
        process(data);
    }
}

例如:100 万条数据,10 台机器分片 → 每台只需处理 10 万条,效率提升 10 倍。

5.2 异步并行执行

在单个任务内部使用多线程并行处理:

@XxlJob("parallelJobHandler")
public void parallelJobHandler() throws Exception {
    List<Task> tasks = getTasks();

    // 使用线程池并行处理
    CountDownLatch latch = new CountDownLatch(tasks.size());
    ExecutorService pool = Executors.newFixedThreadPool(10);

    for (Task task : tasks) {
        pool.execute(() -> {
            try {
                doProcess(task);
            } finally {
                latch.countDown();
            }
        });
    }

    latch.await();
    pool.shutdown();
}

5.3 其他优化手段

手段说明
路由策略优化使用故障转移/忙碌转移,避免将任务分配到故障或繁忙节点
失败重试任务失败自动重试,避免人工介入,提高整体成功率
任务超时控制设置合理的超时时间,避免任务卡死占用资源
取消超时任务Admin 主动终止超时未完成的任务,释放执行器资源
执行器自动发现新增执行器节点后自动注册,无需手动配置,方便弹性扩容

6. XxlJob 的注解有哪些?

Q:XxlJob 常用的注解有哪些?分别怎么用?

A:

6.1 @XxlJob(核心注解)

标记一个方法为 XxlJob 任务处理器。

@XxlJob(value = "jobHandler名称", init = "初始化方法名", destroy = "销毁方法名")
public void myJobHandler() throws Exception {
    // 任务逻辑
}
属性说明示例
valueJobHandler 名称,必填,需与控制台配置一致"demoJobHandler"
init任务初始化方法名,任务线程初始化时调用"init"
destroy任务销毁方法名,任务线程销毁时调用"destroy"

6.2 完整示例(含 init/destroy)

@Component
public class MyXxlJob {

    @XxlJob(value = "myJobHandler", init = "init", destroy = "destroy")
    public void myJobHandler() throws Exception {
        XxlJobHelper.log("任务执行中...");
        String param = XxlJobHelper.getJobParam();  // 获取控制台传递的参数
        // 业务逻辑...
    }

    private void init() {
        XxlJobHelper.log("初始化资源,如数据库连接、线程池等");
    }

    private void destroy() {
        XxlJobHelper.log("释放资源,如关闭连接、销毁线程池等");
    }
}

6.3 XxlJobHelper 常用方法(非注解但常考)

虽然不是注解,但在任务方法中频繁使用:

XxlJobHelper.getJobParam();           // 获取任务参数
XxlJobHelper.getJobId();              // 获取任务ID
XxlJobHelper.getShardTotal();         // 获取总分片数
XxlJobHelper.getShardIndex();         // 获取当前分片索引
XxlJobHelper.log("日志内容");          // 记录任务日志(可在控制台查看)
XxlJobHelper.handleSuccess("成功信息"); // 标记任务成功
XxlJobHelper.handleFail("失败原因");   // 标记任务失败
XxlJobHelper.handleMsg("自定义信息");  // 设置任务执行结果信息

面试注意:XxlJob 只有 @XxlJob 一个注解,结构非常简洁。但需要熟悉 XxlJobHelper 工具类的使用。