2

我们使用 Spring Shedlock 库在集群环境中一次运行一个任务实例。但看起来锁定一直保持到特定时间(由 lockAtMostFor 属性配置,如果我们不配置它从 spring config 获取默认值)直到任务完成。

可以说,如果我配置了 lockAtMostFor=15 分钟,则没有并行任务在任何节点上运行 15 分钟,但在 15 分钟后,如果任何节点有机会运行(或手动触发)任务,即使之前的任务不是,它也能够启动它完成了它的处理。

1. Shedlock 是基于时间的吗?

2.文档(https://github.com/lukas-krecan/ShedLock)说只有在任务完成时才释放锁,并且 lockAtMostFor 属性仅用于节点终止事件的情况,但即使任务是,它也不会那样工作未完成其他节点可以运行任务,只有在没有经过时间时才会阻止其他节点运行任务(lockAtMostFor 属性)

4

2 回答 2

2

您可以在 Shedlock 的文档中找到,为了延长最大锁定时间lockAtMostUntil,您必须手动创建SimpleLock和方法。extend()请记住,您还必须extend()手动调用该方法,或者通过实现一个check()方法来检查您的进程是否完成并在需要时调用该方法,否则,无论您的进程是否extend()到达时间,锁都会自动解锁lockAtMostUntil仍在运行。

在这里,我将解释如何SimpleLock手动从LockProvider. 关于如何设置数据库的问题,我建议您阅读 Shedlock 的这部分文档

我已经用于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>

配置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());
    }
}

这里的DistributedLockSimpleLock是我们实现和所有需要的相关方法的地方:

@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!";
    }
}
于 2021-05-26T10:52:54.997 回答
0

从文档中引用:

您必须将 lockAtMostFor 设置为比正常执行时间长得多的值。如果任务花费的时间超过 lockAtMostFor 所产生的行为可能是不可预测的(多于一个进程将有效地持有锁)。

于 2020-05-21T15:57:35.563 回答