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,你必须同时创建SimpleLockextend()方法手动。请记住,您还必须手动调用 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!";
    }
}