当前位置: 首页 > news >正文

Spring Boot3 实现定时任务 每10分钟执行一次,同时要解决分布式的问题 区分不同场景

在Spring Boot 3中实现分布式定时任务,确保多实例环境下任务仅执行一次,可以采用以下方案:


方案一:Redis分布式锁(推荐)

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.concurrent.TimeUnit;@Component
public class DistributedScheduler {private final StringRedisTemplate redisTemplate;private static final String LOCK_KEY = "TASK_LOCK:MY_TASK";private static final int LOCK_TIMEOUT = 9; // 锁超时时间(分钟)public DistributedScheduler(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}@Scheduled(cron = "0 */10 * * * *")public void scheduledTask() {Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(LOCK_KEY, "locked", Duration.ofMinutes(LOCK_TIMEOUT));if (lockAcquired != null && lockAcquired) {try {// 执行任务逻辑performTask();} finally {// 任务完成后手动释放锁(可选)// redisTemplate.delete(LOCK_KEY);}}}private void performTask() {// 具体任务代码System.out.println("Task executed at: " + new Date());}
}

依赖配置:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

关键点:

  1. 使用 setIfAbsent 原子性操作获取锁,避免并发问题。
  2. 设置锁的自动过期时间(略小于任务间隔),防止死锁。
  3. 根据业务需求选择是否手动释放锁(如任务执行时间可能超过锁超时时间)。

方案二:数据库乐观锁

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;@Component
public class DatabaseLockScheduler {@Scheduled(cron = "0 */10 * * * *")@Transactionalpublic void scheduledTask() {// 1. 查询最近一次任务记录TaskLock lastLock = taskLockRepository.findTopByTaskNameOrderByExecuteTimeDesc("MY_TASK");// 2. 检查是否已执行过if (lastLock != null && lastLock.getExecuteTime().isAfter(LocalDateTime.now().minusMinutes(10))) {return;}// 3. 插入新记录(利用唯一约束或版本号控制并发)TaskLock newLock = new TaskLock("MY_TASK", LocalDateTime.now());taskLockRepository.save(newLock);// 执行任务逻辑performTask();}
}

实体类示例:

@Entity
public class TaskLock {@Idprivate String taskName;private LocalDateTime executeTime;@Versionprivate Integer version;// 省略构造方法/getter/setter
}

关键点:

  1. 使用数据库唯一约束(复合唯一索引)或版本号控制并发。
  2. 需要处理可能的异常(如唯一约束冲突)。

方案三:Quartz集群模式

配置步骤:

  1. 添加依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
  1. 配置数据库存储(application.properties):
spring.quartz.job-store-type=jdbc
spring.quartz.properties.org.quartz.jobStore.isClustered=true
spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=20000
  1. 定义任务:
public class MyJob implements Job {@Overridepublic void execute(JobExecutionContext context) {// 任务逻辑}
}
  1. 配置调度器:
@Configuration
public class QuartzConfig {@Beanpublic JobDetail jobDetail() {return JobBuilder.newJob(MyJob.class).withIdentity("myTask").storeDurably().build();}@Beanpublic Trigger trigger() {return TriggerBuilder.newTrigger().forJob(jobDetail()).withSchedule(CronScheduleBuilder.cronSchedule("0 */10 * * * ?")).build();}
}

方案对比

方案优点缺点
Redis锁实现简单,性能高依赖Redis,需处理锁续期问题
数据库锁无需额外中间件数据库压力大,需处理并发冲突
Quartz集群官方集群支持,功能强大配置复杂,依赖数据库表结构

选择建议:

  • 轻量级场景优先使用Redis锁
  • 已有数据库基础设施可考虑数据库锁
  • 复杂调度需求选择Quartz集群

相关文章:

  • OS7.【Linux】基本指令入门(6)
  • 启发式算法-模拟退火算法
  • 【LLM】Open WebUI 使用指南:详细图文教程
  • OpenCV 图形API(79)图像与通道拼接函数-----将一个三通道的 GMat 图像拆分为三个单独的单通道 GMat函数split3()
  • win11 怎样把D盘空间分给C盘一点
  • 微信小程序BLE蓝牙模块断开后无法再次搜索到原来的蓝牙
  • 使用原生 CSS 实现轮播
  • iPhone或iPad想要远程投屏到Linux系统电脑,要怎么办?
  • PrimExpr 与 RelayExpr 的区别
  • unix 详解
  • R 语言科研绘图第 45 期 --- 桑基图-和弦
  • 共享会议室|物联网解决方案:打造高效、智能的会议空间!
  • 基于深度学习的图像识别技术:从原理到应用
  • 创建简易个人关系图谱(Neo4j )
  • 神经网络之激活函数:解锁非线性奥秘的关键
  • 第三节:Vben Admin 最新 v5.0 对接后端登录接口(下)
  • 微机控制技术复习【一】
  • 【踩坑记录】项目Bug分析:一次因 `String.isBlank()` 引发的崩溃(No such instance method: ‘isBlank‘)
  • Java项目部署-Springboot+Vue网页部署上线全教程
  • 解释 RESTful API,以及如何使用它构建 web 应用程序。
  • 陈雯出任外交部离退休干部局局长,此前为外交部办公厅副主任
  • 水中托举救出落水孩童后遇难,42岁退役军人高武被确认为见义勇为
  • 中国电信财务部总经理周响华调任华润集团总会计师
  • 中国公民免签赴马来西亚的停留天数如何计算?使馆明确
  • “高校领域突出问题系统整治”已启动,聚焦招生、基建、师德等重点
  • 台湾花莲县海域发生5.7级地震,震源深度15公里