背景
在一个有多个服务实例的分布式系统中,如果你用 @Scheduled
来定义定时任务,所有实例都会执行这个任务。ShedLock
的目标是只让一个实例在某一时刻执行这个定时任务。
使用步骤
引入依赖
当前以redisTemplate为例子,MongoDB、Zookeeper、JDBC都可以但是需要建表
需要注意java1.8需要使用4.42.0,java17使用5.10.0不然会报错
Spring常见错误:类文件具有错误的版本 61.0, 应为 52.0
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-redis-redisson</artifactId>
<version>4.42.0</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>4.42.0</version>
</dependency>
增加配置类
package com.platform.common.redis.configure;
import net.javacrumbs.shedlock.core.LockProvider;
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.connection.RedisConnectionFactory;
@Configuration
public class ShedLockConfig {
@Bean
public LockProvider lockProvider(RedisConnectionFactory redisConnectionFactory) {
// 创建 Redis 锁提供者,使用 RedisConnectionFactory
return new RedisLockProvider(redisConnectionFactory);
}
}
启用ShedLock,在启动类上增加@EnableSchedulerLock注解
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
@SpringBootApplication
public class PlatFormRsyncApplication implements CommandLineRunner
定时任务的使用,在@Scheduled下增加@SchedulerLock
注解
需要注意所有@SchedulerLock修饰的方法必须为public,因为需要序列化
@Scheduled(fixedRateString = "${task.fsOrgPersonRsyncTask.fixedRate}")
@SchedulerLock(name = "myTask", lockAtMostFor = "PT30S", lockAtLeastFor = "PT5S")
public void fsOrgPersonRsyncTask() {
name:任务唯一标识(必须)
- lockAtMostFor:最多锁定多久(即使服务挂了,锁也会过期)
- lockAtLeastFor:至少锁定多久(任务执行太快也保持锁一段时间)
格式支持 ISO-8601,例如:
- “PT30S” 表示 30 秒
- “PT5M” 表示 5 分钟
工作原理
不需要手动创建 Redis key,ShedLock 会自动处理
锁的获取:
- 当某个服务器尝试执行定时任务时,ShedLock 会尝试在 Redis 中为该任务设置一个锁(通过设置一个具有过期时间的键)。
- 如果 Redis 中没有该锁,ShedLock 就会成功地为该任务设置锁,表明该服务器成功获得了执行任务的权限。
- 如果 Redis 中已经存在该锁(表示其他服务器正在执行该任务),那么当前尝试获取锁的服务器就无法获取到锁。此时,该服务器不会执行任务,它会等待锁释放。
任务执行:
- 获取到锁的服务器会执行定时任务。在任务执行期间,其他服务器无法获得锁,因此不能执行相同的任务。
锁的释放:
- 任务执行完成后,获取到锁的服务器会释放锁(ShedLock 会自动处理锁的过期和释放)。锁的过期时间是由 lockAtMostFor 属性控制的,如果任务执行时间较短,锁会提前过期释放。
其他服务器:
- 其他服务器一开始获取不到锁时会等到下次尝试。通常,它们会继续等待锁的释放,或者根据配置的定时任务周期(例如 @Scheduled(cron = “0 */1 * * * ?”))再次尝试获取锁。