SpringBoot中@SchedulerLock注解实现定时任务中分布式锁的使用

背景

在SpringBoot项目中经常会去写一些定时任务,但是当我们的服务的实例部署多个的情况下,那么每个实例中的定时任务都会执行一遍,这显然不是我们想要的,我们只想让它执行一次。在没有引入像xxl-job之类的分布式任务调度框架的前提下,并且我们也不想对业务代码进行侵入,那么可以选择shedlock进行尝试使用。

  1. 第一步:引入相关依赖
		<dependency>
            <groupId>net.javacrumbs.shedlock</groupId>
            <artifactId>shedlock-spring</artifactId>
            <version>4.14.0</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>net.javacrumbs.shedlock</groupId>-->
<!--            <artifactId>shedlock-provider-jdbc-template</artifactId>-->
<!--            <version>4.14.0</version>-->
<!--        </dependency>-->
        <dependency>
            <groupId>net.javacrumbs.shedlock</groupId>
            <artifactId>shedlock-provider-redis-spring</artifactId>
            <version>2.5.0</version>
        </dependency>

这里说明一下:ShedLock是支持多种方式进行数据存储的,具体的详情可以看下Github的文档说明:文档
,因为我们项目里面已经引入并使用了Redis,所以我这里就选择了Redis作为存储。所以上边的依赖我也是使用了redis的provider的支持。

  1. 第二步,添加配置类
@Configuration
public class ScheduledLockConfig {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Bean
    public LockProvider lockProvider() {
        return new RedisLockProvider(stringRedisTemplate.getConnectionFactory());
    }
}
  1. 第三步,在启动类上添加注解,否则不会生效
@EnableSchedulerLock(defaultLockAtMostFor = "PT10M")
  1. 第四步,使用
	@Scheduled(cron = "0 0 0 * * *")
    @SchedulerLock(name = "resetPrizeRemainCount", lockAtLeastFor = "60000", lockAtMostFor = "60000")
    public void resetPrizeRemainCount() {
    }

当定时任务开始执行的时候,我们去查看redis你会发现,分布式锁已经生效。过期时间就是上边我们设置的时间。
在这里插入图片描述

当然,从文档中我们看到它还支持自定义provider,锁扩展等等,如有需要可以自行研究。

下面说一下注解具体的参数说明和使用方法:
  • name
    锁的名称,是唯一的标识符。每个任务的锁名称应该唯一,因为它决定了这个锁在分布式环境中的唯一性。
  • lockAtMostFor
    锁的最大保持时间。即使任务执行完成或崩溃,锁也将在指定的时间后自动释放。通常用于确保锁不会因为异常情况而永远无法释放。
    ISO-8601持续时间格式或简单的时间单位格式。例如:PT30S(表示30秒)、PT10M(表示10分钟)或10m(表示10分钟)。
  • lockAtLeastFor
    锁的最小保持时间。在指定的时间内,即使任务执行完成,锁也不会释放。这有助于防止任务被频繁触发(如在短时间内的重复触发)。
  • lockAtLeastForString 和 lockAtMostForString
    这两个参数与 lockAtLeastFor 和 lockAtMostFor 类似,但它们允许通过 Spring 的属性占位符(例如:${lock.duration})来动态配置值。
<think>首先,用户的问题是:“我想了解如何在SpringBoot实现分布式定时任务请问springboot分布式定时任务实现方式”。这表明用户想知道在SpringBoot框架下如何实现分布式定时任务。用户已经提供了一些引用,我需要参考这些引用来构建回答。从引用中,我可以提取关键信息:-引用[1]:提到了SpringBoot实现分布式定时任务之Quartz的基础教程。参考文章包括SpringBoot整合Quartz、配置等。-引用[2]:讨论了SpringBoot分布式定时任务实战,推荐Elastic-Job或SpringScheduler+Redis分布式锁。给出了Elastic-Job的示例配置和代码。-引用[3]:介绍了SpringBoot集成ShedLock实现分布式定时任务,包括其工作原理和应用场景。-引用[4]:提到了SpringBoot集成分布式定时任务,涉及@Configuration和@Bean注解。用户的问题集中在实现方式上,所以我需要提供一种或多种在SpringBoot实现分布式定时任务的方法。系统指令要求:-使用中文回答。-回答结构清晰,帮助用户逐步解决问题。-确保回答真实可靠。-行内数学表达式用$...$,独立公式用$$...$$(但本问题可能不涉及数学公式)。-在引用的段落末尾自然地添加引用标识。-生成相关问题。结构化回答:-开头简要介绍分布式定时任务的重要性。-然后介绍几种常见的实现方式,基于引用:1.使用Quartz:参考引用[1]。2.使用Elastic-Job:参考引用[2]。3.使用ShedLock:参考引用[3]。4.使用SpringSchedulerwithRedisLock:参考引用[2]和[4]的线索。-为每种方式提供简要说明、步骤和代码示例(从引用中提取)。-结束时总结并推荐。-生成相关问题。回答结构:1.**引言**:解释分布式定时任务是什么,为什么需要它。2.**实现方式**:-方式1:QuartzwithSpringBoot-解释Quartz如何工作。-整合步骤(参考引用[1])。-代码示例。-添加引用[^1]。-方式2:Elastic-Job-解释是什么。-步骤(依赖、配置、实现)。-代码示例(从引用[2])。-添加引用[^2]。-方式3:ShedLock-解释和工作原理(引用[3])。-整合步骤。-代码示例。-添加引用[^3]。-方式4:SpringSchedulerwithRedisorDatabaseLock-使用@Scheduled和分布式锁(引用[2]和[4])。-示例。-添加引用[^4]。3.**总结**:比较不同方式,给出建议。4.**生成相关问题**:根据内容生成3-5个相关问题。确保引用标识自然地添加在引用的内容末尾。代码示例:在回答中包含代码块时,要用```包裹,并从引用中复制或适配。中文回答。最终输出格式:-回答部分。-相关问题。</think> ### Spring Boot分布式定时任务实现方式 在分布式系统中实现定时任务时,为了避免多个服务实例重复执行任务(例如数据清理、报表生成等),需要引入分布式锁或集群调度机制。Spring Boot本身支持基本的@Scheduled注解,但它是单机级定时任务。要实现分布式,可以结合以下方案:Quartz、Elastic-Job、ShedLock或自定义分布式锁。以下是每种方式的详细实现步骤和示例代码,基于您提供的引用资料。 --- #### **1. Quartz(使用Spring Boot集成)** Quartz是一个成熟的Java任务调度框架,支持集群模式,通过数据库存储任务状态确保分布式一致性。适合复杂调度场景(如任务分片、重试机制)。 - **工作原理**:多个实例共享一个数据库表,Quartz服务会通过数据库锁协调任务执行,确保同一任务在集群中只被一个实例执行。 - **实现步骤**: 1. **添加依赖**:在pom.xml中添加Quartz和数据库驱动依赖(如MySQL)。 2. **配置数据库连接**:在application.yml中配置Quartz持久化到数据库。 3. **定义定时任务**:继承Job接口实现任务逻辑。 4. **配置Quartz集群**:设置实例ID、锁机制等。 **示例代码**: ```java // 引用[1]整合Quartz示例 import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class MyQuartzJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 分布式任务逻辑,例如清理过期数据 System.out.println("Quartz分布式任务执行: " + new Date()); } } ``` ```yaml # application.yml配置(参考引用[1]) spring: datasource: url: jdbc:mysql://localhost:3306/quartz username: root password: root quartz: job-store-type: jdbc # 启用数据库存储 properties: org.quartz.scheduler.instanceId: AUTO # 自动生成实例ID org.quartz.jobStore.isClustered: true # 启用集群 org.quartz.jobStore.acquireTriggersWithinLock: true # 确保锁机制 ``` - **优点**:高可靠性,支持高并发场景。 - **缺点**:配置复杂,需维护数据库表结构[^1]。 --- #### **2. Elastic-Job(轻量级分布式调度)** Elastic-Job是Apache ShardingSphere的子项目,基于Zookeeper实现分布式协调,适用于高吞吐、弹性分片的场景(如订单超时处理)。 - **工作原理**:任务实例通过Zookeeper注册中心协调锁,支持分片执行(如将数据按ID分片分配到不同实例)。 - **实现步骤**(引用[2]): 1. **添加依赖**:在pom.xml中引入Elastic-Job依赖。 2. **配置Zookeeper**:在application.yml中添加注册中心配置。 3. **实现定时任务类**:继承SimpleJob接口,定义分片逻辑。 **示例代码**: ```java // 引用[2]的OrderTimeoutJob示例 import org.apache.shardingsphere.elasticjob.api.ShardingContext; import org.apache.shardingsphere.elasticjob.simple.job.SimpleJob; public class OrderTimeoutJob implements SimpleJob { @Override public void execute(ShardingContext context) { int shardIndex = context.getShardingItem(); // 当前分片索引 List<Long> orderIds = fetchTimeoutOrders(shardIndex); orderIds.forEach(this::cancelOrder); // 处理超时订单 } private List<Long> fetchTimeoutOrders(int shard) { // 分片查询逻辑,例如数据库按订单ID取模 return orderRepository.findTimeoutOrders(shard); } } ``` ```yaml # application.yml配置(引用[2]) elasticjob: reg-center: server-lists: localhost:2181 # Zookeeper地址 namespace: elasticjob-demo # 命名空间 ``` - **优点**:易扩展,支持动态分片;适合大数据任务。 - **缺点**:依赖Zookeeper,增加运维复杂度[^2]。 --- #### **3. ShedLock(轻量级锁机制)** ShedLock是一个Java库,通过共享存储(如Redis或数据库)实现分布式锁,适合快速集成Spring Scheduler的场景(如定时报告生成)。 - **工作原理**:任务启动时,ShedLock在外部存储中创建锁记录(带过期时间),其他实例发现锁存在则跳过执行。 - **实现步骤**(参考引用[3]): 1. **添加依赖**:引入ShedLock依赖。 2. **配置锁存储**:使用Redis或数据库作为锁提供者。 3. **注解驱动任务**:在Spring Scheduler任务上添加@SchedulerLock注解。 **示例代码**: ```java // 引用[3]集成ShedLock import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class ReportJob { @Scheduled(cron = "0 0 1 * * ?") // 每天1点执行 @SchedulerLock(name = "reportTask", lockAtMostFor = "10m", lockAtLeastFor = "5m") public void generateReport() { // 生成日报逻辑 System.out.println("分布式生成日报: " + LocalDateTime.now()); } } ``` ```java // 配置LockProvider(引用[4]的@Bean方法) import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.RedisTemplate; @Configuration public class ShedLockConfig { @Bean public LockProvider lockProvider(RedisTemplate<String, String> redisTemplate) { return new RedisLockProvider(redisTemplate.getConnectionFactory()); } } ``` - **优点**:简单快速,无中间件依赖(可选存储)。 - **缺点**:锁过期时间需精确设置,防止任务重叠[^3]。 --- #### **4. Spring Scheduler + 分布式锁(自定义实现)** 如果追求轻量级,可直接使用Spring自带的@Scheduled配合Redis或数据库锁,适用于简单任务(如定期清理缓存)。 - **工作原理**:任务执行前通过Redis或数据库锁检查,获取锁成功才执行任务。 - **实现步骤**(参考引用[2]和[4]): 1. **添加Redis依赖**:引入spring-boot-starter-data-redis。 2. **创建锁工具类**:实现基于Redis的分布式锁。 3. **注解任务并加锁**:在@Scheduled方法中调用锁检查。 **示例代码**: ```java // Spring Scheduler with Redis锁(参考引用[2]和[4]) import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.data.redis.core.RedisTemplate; @Component public class CacheCleanJob { private final RedisTemplate<String, String> redisTemplate; public CacheCleanJob(RedisTemplate<String, String> redisTemplate) { this.redisTemplate = redisTemplate; } @Scheduled(fixedRate = 300000) // 每5分钟执行 public void cleanCache() { String lockKey = "clean-cache-lock"; Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", 30, TimeUnit.SECONDS); // 锁过期时间30秒 if (lockAcquired != null && lockAcquired) { try { // 执行清理逻辑 System.out.println("Redis分布式锁获取成功: 清理缓存"); } finally { redisTemplate.delete(lockKey); // 释放锁 } } } } ``` - **优点**:无需额外框架,适用于小型项目。 - **缺点**:需手动处理锁逻辑,可靠性略低[^2][^4]。 --- #### **总结与建议** - **方案选择**: - **推荐轻量级场景**:ShedLock或Spring Scheduler + Redis,快速易用。 - **高可靠场景**:Quartz或Elastic-Job,支持集群分片。 - **关键考量**:系统复杂度、是否需要任务持久化或分片。 - **性能优化**:确保锁过期时间合理(参考ShedLock的lockAtMostFor配置),避免死锁;测试环境中用嵌入式Redis或H2数据库简化开发。 实现分布式定时任务的核心是确保任务在集群中的排他性。根据您的项目需求选择合适的方案,并参考官方文档验证配置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值