@SchedulerLock 防止分布式环境下定时任务并发执行

发布于:2025-04-12 ⋅ 阅读:(39) ⋅ 点赞:(0)

背景

在一个有多个服务实例的分布式系统中,如果你用 @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 * * * ?”))再次尝试获取锁。

网站公告

今日签到

点亮在社区的每一天
去签到