Skip to content
第 60 章 后端 ⏱ 8 分钟阅读

第 60 章:定时任务 ​

学习目标 ​

  • 用 Spring Schedule 做简单定时
  • 用 Xxl-Job 做分布式定时
  • 避免重复执行

一、Spring Schedule(单机) ​

java
@SpringBootApplication
@EnableScheduling
public class App { }

@Component
public class MyTask {

    @Scheduled(fixedRate = 5000)             // 每 5 秒
    public void report() {
        log.info("统计: {}", LocalDateTime.now());
    }

    @Scheduled(cron = "0 0 2 * * ?")         // 每天凌晨 2 点
    public void clean() {
        log.info("清理过期数据");
    }
}

cron 表达式(6/7 位):

秒 分 时 日 月 周 [年]
0  0  2  *  *  ?     每天 2 点
0  0  9  *  *  MON   每周一 9 点(注意周位置用 ?)
*/10 * * * * *       每 10 秒

工具:cron 在线生成

⚠️ 坑 1:多机部署时 Spring Schedule 会重复执行。N 台机器 = N 次执行。

二、Xxl-Job(分布式) ​

架构:调度中心 + 执行器。

[Admin 调度中心] -- 调度 --> [Executor 执行器]
                                ↓
                            你的应用

2.1 部署 Admin ​

bash
docker run -d --name xxl-job-admin -p 8080:8080 \
  -e PARAMS="--spring.datasource.url=jdbc:mysql://...xxl_job..." \
  xuxueli/xxl-job-admin:2.4.0

2.2 应用集成 ​

xml
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>2.4.0</version>
</dependency>
yaml
xxl:
  job:
    admin:
      addresses: http://localhost:8080/xxl-job-admin
    executor:
      appname: my-app
      port: 9999
      logpath: /data/applogs/xxl-job
java
@Configuration
public class XxlJobConfig {
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        XxlJobSpringExecutor executor = new XxlJobSpringExecutor();
        executor.setAdminAddresses("http://localhost:8080/xxl-job-admin");
        executor.setAppname("my-app");
        executor.setPort(9999);
        executor.setLogPath("/data/applogs/xxl-job");
        return executor;
    }
}

2.3 写任务 ​

java
@Component
public class MyJobHandler {

    @XxlJob("myJob")
    public void execute() {
        XxlJobHelper.log("开始执行");
        try {
            // 业务逻辑
            XxlJobHelper.handleSuccess();
        } catch (Exception e) {
            XxlJobHelper.handleFail("失败: " + e.getMessage());
        }
    }
}

2.4 控制台配置 ​

  1. 进入 Admin:http://localhost:8080/xxl-job-admin
  2. 任务管理 → 新增 → 填 JobHandler=myJob、Cron、路由策略
  3. 启动,执行器自动注册

三、路由策略(解决多机执行) ​

策略行为
第一个只在第一台机器执行
最后一个只在最后一台执行
轮询轮询分到不同机器
分片广播所有机器都执行,各自处理一部分数据
一致性哈希按 ID 哈希分
java
@XxlJob("shardingJob")
public void shardingJob() {
    int shardIndex = XxlJobHelper.getShardIndex();      // 当前分片
    int shardTotal = XxlJobHelper.getShardTotal();      // 总分片数

    // 只处理 index % shardTotal == shardIndex 的数据
    List<Long> ids = ...
    for (Long id : ids) {
        if (id % shardTotal == shardIndex) {
            process(id);
        }
    }
}

四、阻塞处理策略 ​

策略行为
单机串行排队,等上一个执行完
丢弃后续取消当前
覆盖之前杀旧的,起新的

⚠️ 坑 2:避免并发问题,选「单机串行」。

五、为什么不用 @Scheduled 后接 RequestMapping ​

java
// ❌ 定时任务调 HTTP 接口
@Scheduled(fixedRate = 5000)
public void sync() {
    restTemplate.getForEntity("http://localhost:8080/api/sync");
}

为什么错?需要 HTTP,慢、超时、失败无法控制。

直接调 Service:

java
@XxlJob("sync")
public void sync() {
    syncService.execute();    // 直接调本地方法
}

六、本章小结 ​

要点关键
Spring Schedule单机,但多机会重复
Xxl-Job分布式,Admin 控制
分片处理大数据量
阻塞单机串行最稳

动手练习 ​

  1. 部署 Xxl-Job Admin(docker)
  2. 写一个定时任务,每 10 秒打印时间

下一章:第 61 章:文件上传与 WebSocket →

本站基于 VitePress 构建 · 由 StackHub 团队维护