ShedLock是一个锁,官方解释是他永远只是一个锁,并非是一个分布式任务调度器。一般shedLock被使用的场景是,你有个任务,你只希望他在单个节点执行,而不希望他并行执行,而且这个任务是支持重复执行的。如对某些查询出来的数据打标签,没有任何的事务性处理操作。以上,官方的解释比较拗口,具体可以参见github上的wiki。
我的理解,ShedLock是一个悲观锁,而无论悲观锁还是乐观锁,他的实现,必须借助于公共存储。
所以,对于shedLock,需要实现分布式锁,他也需要借助于共享存储,目前支持如下几种方式:
所以,这个和zookeeper或者redis的理论基本类似。只是这个shedLock封装的更好,不需要我们去实现过多的客户端。而像zookeeper或者redis,需要我们自己实现。具体可以看我之前写的那篇《任务调度总结》文章。还有我github或者码云上的sample。
shedLock支持注解,和spring结合使用更简单。我们来看看他的使用方式。我们以共享存储使用mysql为例子:
首先,需要在你的项目中引入两个包,假设项目是maven构建:
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>2.2.0</version>
</dependency>
其次,在你的启动类中增加注解支持。
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
稍后会说明下,这个lockatMostFor的含义。
下一步,需要初始化Provider支持。操作数据库。
@Configuration
public class LockProviderConfiuration {
@Autowired DataSource dataSource;
@Bean
public LockProvider lockProvider () {
return new JdbcTemplateLockProvider(dataSource);
}
}
这里全局datasource需要事先创建好。在这里可以直接注入使用。然后实例化jdbcTemplateLockProvider。
建表
CREATE TABLE shedlock(
name VARCHAR(64),
lock_until TIMESTAMP(3) NULL,
locked_at TIMESTAMP(3) NULL,
locked_by VARCHAR(255),
PRIMARY KEY (name)
)
相信看到这里大家都明白了,name是全局唯一的。用这个来标识全局唯一的定时任务。用此来变相实现一个悲观锁。
以上做好了后,可以开始写你的定时任务了,我们这里写一个测试类
@Component
public class RiskAdminScheduler {
private static final String SIXTY_MIN = "PT60M";
private static final String THREE_MIN = "PT3M";
@Scheduled(cron = "0 */1 * * * ? ")
@SchedulerLock(name = "test",lockAtLeastForString = THREE_MIN,lockAtMostForString = SIXTY_MIN)
public void test() {
System.out.println(LocalDateTime.now());
System.out.println(Thread.currentThread().getName()+"--executed......");
}
}
意思就是,当节点挂掉后,这个锁还是要释放的。最长时间就是most设置的,和zookeeper中的临时节点相似,和redis设置超时时间类似。而为了防止集群启动的先后或者各节点 没有做时钟同步,用了least来防止这种情况下,重复起任务的状况。
这里需要注意的是,least时间设置了后,比如我这里是1分钟定时任务执行一次,而least设置是2分钟。那么也就是2分钟是持有锁的最小时间,2分钟后才释放。所以1分钟执行完的定时任务,必须等到2分钟结束,锁释放后,才能再次执行。当然,实际我们业务中不会出现这种每分钟执行的业务相关的定时任务,即使有,那么情况也比较,比如快速掉单查询,以前做支付的时候,那也是支持并发的,不需要分布式锁的。这里提醒下大家注意下使用场景。大部分场景我们还是可以用这个shedlock的。
我们看下表中的数据:
很明显,持有时间是21:06,释放时间是21:09,就是least设置的时间。大家使用的时候注意下。