Spring Shedlock 计时问题
Spring Shedlock timing issue
我们正在使用 Spring Shedlock 库在集群环境中一次 运行 一个任务实例。
但看起来锁定会一直保持到特定时间( 由 lockAtMostFor 属性配置如果我们不配置它会从 spring 配置 中获取默认值)直到任务完成。
假设我配置了 lockAtMostFor=15 分钟
任何节点上没有并行任务 运行s 15 分钟,但在 15 分钟后,如果任何节点有机会 运行(或手动触发)任务它能够启动它,即使之前的任务不是处理完毕。
1.Is Shedlock 是基于时间的?
2.Documentation(https://github.com/lukas-krecan/ShedLock) 表示只有在任务完成时才会释放锁,并且 lockAtMostFor 属性仅在节点终止事件的情况下使用,但它不起作用
这样,即使任务未完成,其他节点也能够 运行 执行任务,其他节点仅在时间未结束时才被阻止执行 运行ning 任务(lockAtMostFor 属性)
引用文档:
您必须将 lockAtMostFor 设置为比正常执行时间长得多的值。如果任务花费的时间比 lockAtMostFor 长,则结果行为可能无法预测(多于一个进程将有效地持有锁)。
你可以在Shedlock的documentation中找到,为了延长最大锁定时间lockAtMostUntil
,你必须同时创建SimpleLock
和extend()
方法手动。请记住,您还必须手动调用 extend()
方法,或者通过实现一个 check()
方法来检查您的进程是否完成并在需要时调用 extend()
方法,否则,锁定到lockAtMostUntil
时间会自动解锁,不管你的进程还在运行.
我在这里解释如何从 LockProvider
手动创建 SimpleLock
。
关于如何设置数据库的问题,我推荐你阅读Shedlockdocumentation的这一部分。
我在我的应用程序中使用了 spring boot
,所以您看到的注释来自 spring boot
。
依赖关系:
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>4.23.0</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>4.23.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.15.RELEASE</version>
</dependency>
配置 of LockProvider
:
@EnableAsync
@Profile("!unit_test")
@Configuration
public class LockConfiguration {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(
JdbcTemplateLockProvider.Configuration.builder()
.withTimeZone(TimeZone.getTimeZone("Europe/Rome"))
.withJdbcTemplate(new JdbcTemplate(dataSource))
.withTableName("shared_lock")
.usingDbTime()
.build());
}
}
DistributedLock 这里是我们实现 SimpleLock
和所有需要的相关方法的地方:
@Component
@Slf4j
public class DistributedLockWithExtend {
@Autowired
private LockProvider lockProvider;
private final Map<String, SimpleLock> locks = new HashMap<>();
public boolean tryLock(String lockname, Duration lockDuration){
Duration minDuration = Duration.ZERO;
Instant now = ClockProvider.now();
LockConfiguration config = new LockConfiguration(now, lockname, lockDuration, minDuration);
Optional<SimpleLock> lockLocal = lockProvider.lock(config);
if(lockLocal.isPresent()) {
locks.put(lockname, lockLocal.get());
log.debug("lock is created!");
return true;
}else {
log.debug("lock is locked!");
return false;
}
}
public boolean extendLock(String lockname, Duration duration){
Duration minDuration = Duration.ZERO;
SimpleLock lock = locks.get(lockname);
if(lock != null) {
Optional<SimpleLock> localLock = lock.extend(duration, duration);
locks.put(lockname, localLock.get());
log.debug("Lock is extended");
return true;
}else {
log.debug("There is no lock or the lock is already unlocked! Create a lock with tryLock() if you need!");
return false;
}
}
public String unLock(String lockname){
SimpleLock lock = locks.get(lockname);
if(lock != null) {
locks.remove(lockname);
lock.unlock();
log.debug("Lock is unLocked!");
return "Lock is unLocked!";
}else {
log.debug("There is no lock or the lock is already unlocked! Create a lock if you need!");
return "There is no lock or the lock is already unlocked! Create a lock if you need!";
}
}
}
这是我创建的测试 RestController 来演示 DistributedLockWithExtend
方法的用法:
@Profile("!unit_test")
@RestController
@RequestMapping(TestLockController.PATH)
public class TestLockController {
public static final String PATH = BaseController.PATH + "/test-lock";
@Autowired
ApplyPolicyScheduler applyPolicySchedular;
@Autowired
DistributedLockWithExtend distributedLockWithExtend;
@GetMapping("/up")
public String testService(){
return "Service is up!";
}
@GetMapping("/invoke")
public String invokeTest(){
return distributedLockWithExtend.tryLock("testLockUtil", Duration.ofMinutes(1)) + "";
}
@GetMapping("/extend")
public String extendTest(){
return distributedLockWithExtend.extendLock("testLockUtil", Duration.ofMinutes(3)) + "";
}
@GetMapping("/unlock")
public String unlockTest(){
distributedLockWithExtend.unLock("testLockUtil");
return "Unlock called!";
}
}
我们正在使用 Spring Shedlock 库在集群环境中一次 运行 一个任务实例。 但看起来锁定会一直保持到特定时间( 由 lockAtMostFor 属性配置如果我们不配置它会从 spring 配置 中获取默认值)直到任务完成。
假设我配置了 lockAtMostFor=15 分钟 任何节点上没有并行任务 运行s 15 分钟,但在 15 分钟后,如果任何节点有机会 运行(或手动触发)任务它能够启动它,即使之前的任务不是处理完毕。
1.Is Shedlock 是基于时间的?
2.Documentation(https://github.com/lukas-krecan/ShedLock) 表示只有在任务完成时才会释放锁,并且 lockAtMostFor 属性仅在节点终止事件的情况下使用,但它不起作用 这样,即使任务未完成,其他节点也能够 运行 执行任务,其他节点仅在时间未结束时才被阻止执行 运行ning 任务(lockAtMostFor 属性)
引用文档:
您必须将 lockAtMostFor 设置为比正常执行时间长得多的值。如果任务花费的时间比 lockAtMostFor 长,则结果行为可能无法预测(多于一个进程将有效地持有锁)。
你可以在Shedlock的documentation中找到,为了延长最大锁定时间lockAtMostUntil
,你必须同时创建SimpleLock
和extend()
方法手动。请记住,您还必须手动调用 extend()
方法,或者通过实现一个 check()
方法来检查您的进程是否完成并在需要时调用 extend()
方法,否则,锁定到lockAtMostUntil
时间会自动解锁,不管你的进程还在运行.
我在这里解释如何从 LockProvider
手动创建 SimpleLock
。
关于如何设置数据库的问题,我推荐你阅读Shedlockdocumentation的这一部分。
我在我的应用程序中使用了 spring boot
,所以您看到的注释来自 spring boot
。
依赖关系:
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>4.23.0</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>4.23.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.15.RELEASE</version>
</dependency>
配置 of LockProvider
:
@EnableAsync
@Profile("!unit_test")
@Configuration
public class LockConfiguration {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(
JdbcTemplateLockProvider.Configuration.builder()
.withTimeZone(TimeZone.getTimeZone("Europe/Rome"))
.withJdbcTemplate(new JdbcTemplate(dataSource))
.withTableName("shared_lock")
.usingDbTime()
.build());
}
}
DistributedLock 这里是我们实现 SimpleLock
和所有需要的相关方法的地方:
@Component
@Slf4j
public class DistributedLockWithExtend {
@Autowired
private LockProvider lockProvider;
private final Map<String, SimpleLock> locks = new HashMap<>();
public boolean tryLock(String lockname, Duration lockDuration){
Duration minDuration = Duration.ZERO;
Instant now = ClockProvider.now();
LockConfiguration config = new LockConfiguration(now, lockname, lockDuration, minDuration);
Optional<SimpleLock> lockLocal = lockProvider.lock(config);
if(lockLocal.isPresent()) {
locks.put(lockname, lockLocal.get());
log.debug("lock is created!");
return true;
}else {
log.debug("lock is locked!");
return false;
}
}
public boolean extendLock(String lockname, Duration duration){
Duration minDuration = Duration.ZERO;
SimpleLock lock = locks.get(lockname);
if(lock != null) {
Optional<SimpleLock> localLock = lock.extend(duration, duration);
locks.put(lockname, localLock.get());
log.debug("Lock is extended");
return true;
}else {
log.debug("There is no lock or the lock is already unlocked! Create a lock with tryLock() if you need!");
return false;
}
}
public String unLock(String lockname){
SimpleLock lock = locks.get(lockname);
if(lock != null) {
locks.remove(lockname);
lock.unlock();
log.debug("Lock is unLocked!");
return "Lock is unLocked!";
}else {
log.debug("There is no lock or the lock is already unlocked! Create a lock if you need!");
return "There is no lock or the lock is already unlocked! Create a lock if you need!";
}
}
}
这是我创建的测试 RestController 来演示 DistributedLockWithExtend
方法的用法:
@Profile("!unit_test")
@RestController
@RequestMapping(TestLockController.PATH)
public class TestLockController {
public static final String PATH = BaseController.PATH + "/test-lock";
@Autowired
ApplyPolicyScheduler applyPolicySchedular;
@Autowired
DistributedLockWithExtend distributedLockWithExtend;
@GetMapping("/up")
public String testService(){
return "Service is up!";
}
@GetMapping("/invoke")
public String invokeTest(){
return distributedLockWithExtend.tryLock("testLockUtil", Duration.ofMinutes(1)) + "";
}
@GetMapping("/extend")
public String extendTest(){
return distributedLockWithExtend.extendLock("testLockUtil", Duration.ofMinutes(3)) + "";
}
@GetMapping("/unlock")
public String unlockTest(){
distributedLockWithExtend.unLock("testLockUtil");
return "Unlock called!";
}
}