ShedLock简介

ShedLock 保证任务最多同时执行一次,当一个节点获取到任务并开始执行,那么其他节点不会等待,而是直接跳过。

它不是一个分布式调度程序,更像是一个分布式锁,在分布式锁的基础上对调度任务做了扩展,使得分布式服务下的定时任务看起来像分布式调度任务一样。

此外,ShedLock 的锁是基于时间的,并且 ShedLock 在乐观上是假设所有节点上的时钟是同步的。

ShedLock 的Github地址:https://github.com/lukas-krecan/ShedLock

ShedLock组成

ShedLock 分为三部分:

  • Core 核心:提供锁机制。
  • Integration 集成:使用 Spring AOP、Micronaut AOP 或手写代码与业务项目代码集成。
  • Lock Provider 锁提供程序:使用 SQL 数据库、Mongo、Redis 等外部进程提供锁。

ShedLock用法

ShedLock 基于 Spring 实现时,由于 Spring 6 的JDK依赖因素,所以 ShedLock 的5.x版本需要 JDK > 17 ,否则需要使用 ShedLock 4.x 版本。

接下来,以 Spring 项目(并非Spring Boot项目)为例,JDK 版本为 17 ,Spring 版本为 6.1.1 。

示例项目代码地址:https://github.com/wangfarui/work-report/tree/main/shedlock-schedule

启用并配置 Scheduled locking

首先需要添加 ShedLock 依赖。

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>5.10.2</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-redis-spring</artifactId>
<version>5.10.2</version>
</dependency>

然后,shedlock-provider-redis-spring 中依赖于 spring-data-redis,其中 redis 的驱动器是可选的,默认有 jedis 和 lettuce 两种。示例项目使用 jedis,所以还需要添加 jedis 的依赖。

1
2
3
4
5
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.1.0</version>
</dependency>

接下来,开始配置 ShedLock 。第一步注入LockProvider

1
2
3
4
@Bean
public LockProvider lockProvider(RedisConnectionFactory connectionFactory) {
return new RedisLockProvider(connectionFactory, "local");
}

通过上诉代码可以发现,LockProvider基于 Redis 的实现类 RedisLockProvider 在实例化时还需要一个 RedisConnectionFactory 对象。第二步需要注入 jedis 的 ConnectionFactory:

1
2
3
4
5
6
@Bean
public JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration("localhost", 6379);
configuration.setPassword("");
return new JedisConnectionFactory(configuration);
}

配置完 ShedLock 的 LockProvider 之后,在配置类上标记 @EnableSchedulerLock以启用 @SchedulerLock 注解的锁功能,标记@EnableScheduling以启用@Scheduled注解的调度功能。

1
2
3
4
5
@Configuration
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "10m")
public class MySpringConfiguration {
}

配置定时任务

通过 @Scheduled 标记Bean对象下的方法为一个定时任务,再通过 @SchedulerLock 使得该任务在分布式服务下同时最多执行一次。

1
2
3
4
@Scheduled(cron = "0/5 * * * * *")
@SchedulerLock(name = "scheduledTaskName")
public void scheduledTask() {
}

ShedLock总结

使用 ShedLock 大致分为三步:

  1. 提供一个 LockProvider
  2. 标记 @EnableSchedulerLock 注解以启用@SchedulerLock的注解功能。
  3. 标记 @SchedulerLock 注解以启用锁功能。

以上操作步骤都是按照 ShedLock 默认配置来进行的,如果需要启用高级用法,例如希望@SchedulerLock注解只作用于@Scheduled注解下,可以指定@EnableSchedulerLock 注解的 interceptMode() 属性为 InterceptMode.PROXY_SCHEDULER 。