news 2026/4/18 16:55:57

SpringBoot 集成 ShedLock @SchedulerLock 分布式锁(基于Redis的方式)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot 集成 ShedLock @SchedulerLock 分布式锁(基于Redis的方式)

1. 为什么需要分布式定时任务锁

在微服务架构中,我们经常会部署多个相同的服务实例来实现高可用。这时候如果服务中有定时任务,就会遇到一个典型问题:所有实例的定时任务会在同一时间触发,导致任务被重复执行。比如每天凌晨的报表生成任务,如果部署了3个实例,就可能生成3份相同的报表,这显然不是我们想要的结果。

我去年就遇到过这样的生产事故。当时一个数据同步任务被部署在5个节点上,由于没有加锁,导致相同的数据被反复同步了5次,不仅造成资源浪费,还引发了数据一致性问题。后来我们引入了ShedLock这个轻量级解决方案,完美解决了分布式环境下的定时任务重复执行问题。

ShedLock的工作原理很简单:它通过在Redis中设置一个临时锁来标记当前正在执行的任务。其他节点在执行相同任务时,会先检查这个锁是否存在。如果锁存在就直接跳过执行,避免重复劳动。这种机制就像多人协作时使用的"会议室预定系统"——第一个预定会议室的人会把会议室标记为"已占用",其他人看到这个标记就会选择其他时间。

2. 环境准备与依赖配置

2.1 基础环境要求

在开始集成之前,请确保你的开发环境满足以下条件:

  • JDK 1.8或更高版本
  • Maven 3.5+
  • SpringBoot 2.x
  • Redis服务器(可以是单机或集群)

我建议使用SpringBoot 2.3.4.RELEASE版本,这个版本经过长期验证比较稳定。如果你用的是SpringBoot 3.x,需要注意部分API可能有变化。

2.2 添加Maven依赖

首先需要在pom.xml中添加以下依赖:

<!-- ShedLock核心库 --> <dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-spring</artifactId> <version>4.44.0</version> </dependency> <!-- Redis锁实现 --> <dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-provider-redis-spring</artifactId> <version>4.44.0</version> </dependency> <!-- Spring Data Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- 连接池 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>

这里我推荐使用4.44.0版本,这是目前最新的稳定版。如果你项目已经使用了Jedis或Lettuce,可以省略spring-boot-starter-data-redis依赖。

3. Redis锁配置详解

3.1 基础配置类

创建一个配置类来设置Redis锁提供者:

import net.javacrumbs.shedlock.core.LockProvider; import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider; import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; @Configuration @EnableSchedulerLock(defaultLockAtMostFor = "PT30S") public class ShedLockConfig { @Bean public LockProvider lockProvider(RedisConnectionFactory connectionFactory) { return new RedisLockProvider(connectionFactory, "my-app"); } }

这里有几个关键点需要注意:

  1. @EnableSchedulerLock注解开启了ShedLock功能
  2. defaultLockAtMostFor设置了默认的最大锁持有时间为30秒
  3. RedisLockProvider构造函数的第二个参数是环境标识,建议使用应用名

3.2 锁时间参数详解

ShedLock使用ISO8601持续时间格式来设置时间参数,这种格式看起来可能有点奇怪,但其实很容易理解:

  • PT10S = 10秒
  • PT5M = 5分钟
  • PT1H = 1小时

在实际项目中,我建议这样设置时间参数:

  • lockAtLeastFor:设置为任务平均执行时间的80%
  • lockAtMostFor:设置为任务最长可能执行时间的120%

这样可以避免任务执行时间波动导致的锁问题。

4. 定时任务实现

4.1 启用定时任务

首先确保在启动类上添加@EnableScheduling注解:

@SpringBootApplication @EnableScheduling public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }

4.2 编写带锁的定时任务

下面是一个完整的定时任务示例:

import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class ReportGenerationTask { @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行 @SchedulerLock( name = "dailyReportGeneration", lockAtLeastFor = "PT20M", // 最少锁定20分钟 lockAtMostFor = "PT30M" // 最多锁定30分钟 ) public void generateDailyReport() { // 这里写报表生成逻辑 System.out.println("开始生成每日报表..."); // 模拟耗时操作 try { Thread.sleep(1000 * 60 * 15); // 15分钟 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("每日报表生成完成"); } }

4.3 参数调优建议

根据我的经验,设置锁时间时有几个最佳实践:

  1. 对于短周期任务(如每分钟执行),lockAtLeastFor可以设置为周期的一半
  2. 对于长周期任务(如每天执行),lockAtLeastFor可以设置为预估执行时间的1.5倍
  3. 总是设置lockAtMostFor,这是防止节点崩溃导致锁无法释放的安全机制

5. 测试与验证

5.1 本地多实例测试

为了验证分布式锁是否生效,我们可以启动多个实例来测试:

  1. 在IDEA中,复制一个启动配置
  2. 在VM options中添加-Dserver.port=8081
  3. 同时启动两个实例

观察日志输出,应该只有一个实例会执行定时任务。

5.2 查看Redis中的锁

可以通过Redis客户端查看锁的状态:

redis-cli KEYS shedlock*

你会看到类似这样的键:

shedlock:dailyReportGeneration

可以使用TTL命令查看锁的剩余时间:

TTL shedlock:dailyReportGeneration

5.3 常见问题排查

如果发现锁不生效,可以检查以下几点:

  1. Redis连接是否正常
  2. 任务名称是否唯一
  3. 时间参数设置是否合理
  4. 系统时钟是否同步(在分布式环境中非常重要)

6. 生产环境最佳实践

6.1 Redis高可用配置

在生产环境中,建议使用Redis哨兵或集群模式:

@Bean public LockProvider lockProvider(RedisConnectionFactory connectionFactory) { return new RedisLockProvider.Builder(connectionFactory) .environment("prod") .build(); }

6.2 监控与告警

建议对以下指标进行监控:

  1. 任务执行成功率
  2. 任务执行时间
  3. 锁等待时间

可以使用Spring Boot Actuator来暴露这些指标。

6.3 性能优化

对于高频任务,可以考虑以下优化:

  1. 减小锁的粒度(为不同任务设置不同的锁)
  2. 适当缩短锁的持有时间
  3. 使用本地缓存减少Redis访问

我在一个电商项目中通过优化锁配置,将定时任务的吞吐量提升了40%。

7. 高级用法与扩展

7.1 自定义锁提供者

如果需要更复杂的锁逻辑,可以实现自己的LockProvider:

public class CustomLockProvider implements LockProvider { @Override public Optional<SimpleLock> lock(LockConfiguration lockConfig) { // 自定义加锁逻辑 } }

7.2 与Spring Retry集成

对于可能失败的任务,可以结合Spring Retry实现重试机制:

@Retryable(maxAttempts=3, backoff=@Backoff(delay=1000)) @SchedulerLock(name = "retryableTask") public void retryableTask() { // 可能失败的任务逻辑 }

7.3 动态任务配置

通过配置中心可以实现动态调整任务参数:

@Scheduled(cron = "${reports.cron}") @SchedulerLock( name = "dynamicTask", lockAtLeastForString = "${reports.minLockTime}", lockAtMostForString = "${reports.maxLockTime}" ) public void dynamicTask() { // 任务逻辑 }

在实际项目中,这种动态配置特别有用,可以不用重启服务就能调整任务执行策略。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 16:41:17

3分钟搞定:Windows 11 LTSC完整恢复微软商店终极方案

3分钟搞定&#xff1a;Windows 11 LTSC完整恢复微软商店终极方案 【免费下载链接】LTSC-Add-MicrosoftStore Add Windows Store to Windows 11 24H2 LTSC 项目地址: https://gitcode.com/gh_mirrors/ltscad/LTSC-Add-MicrosoftStore Windows 11 LTSC&#xff08;长期服务…

作者头像 李华
网站建设 2026/4/18 16:41:03

fre:ac音频转换器:从新手到专家的完整实战指南

fre:ac音频转换器&#xff1a;从新手到专家的完整实战指南 【免费下载链接】freac The fre:ac audio converter project 项目地址: https://gitcode.com/gh_mirrors/fr/freac fre:ac是一款功能强大的免费开源音频转换工具&#xff0c;支持Windows、macOS、Linux和FreeBS…

作者头像 李华
网站建设 2026/4/18 16:40:54

如何在10分钟内使用MT3完成专业级音乐转录:终极指南

如何在10分钟内使用MT3完成专业级音乐转录&#xff1a;终极指南 【免费下载链接】mt3 MT3: Multi-Task Multitrack Music Transcription 项目地址: https://gitcode.com/gh_mirrors/mt/mt3 MT3&#xff08;Multi-Task Multitrack Music Transcription&#xff09;是Goog…

作者头像 李华