JDBC Lock 注册表

4.3 版本引入了 `JdbcLockRegistry`。某些组件(例如,聚合器和重排序器)使用从 `LockRegistry` 实例获取的锁来确保一次只有一个线程操作一个组。`DefaultLockRegistry` 在单个组件内部执行此功能。现在你可以在这些组件上配置外部锁注册表。与共享的 `MessageGroupStore` 一起使用时,可以使用 `JdbcLockRegistry` 在多个应用程序实例之间提供此功能,从而确保一次只有一个实例可以操作该组。

当锁由本地线程释放时,另一个本地线程通常可以立即获取锁。如果锁由使用不同注册表实例的线程释放,则可能需要长达 100 毫秒才能获取锁。

`JdbcLockRegistry` 基于 `LockRepository` 抽象,它有一个 `DefaultLockRepository` 实现。数据库 schema 脚本位于 `org.springframework.integration.jdbc` 包中,该包针对特定的 RDBMS 供应商进行了划分。例如,以下清单显示了锁定表的 H2 DDL

CREATE TABLE INT_LOCK  (
    LOCK_KEY CHAR(36),
    REGION VARCHAR(100),
    CLIENT_ID CHAR(36),
    CREATED_DATE TIMESTAMP NOT NULL,
    constraint INT_LOCK_PK primary key (LOCK_KEY, REGION)
);

`INT_` 可以根据目标数据库设计要求进行更改。因此,你必须在 `DefaultLockRepository` bean 定义上使用 `prefix` 属性。

有时,某个应用程序会进入无法释放分布式锁并删除数据库中特定记录的状态。为此,此类死锁可以在下次锁定调用时由其他应用程序过期。`DefaultLockRepository` 上的 `timeToLive` (TTL) 选项就是为此目的提供的。你可能还想为给定 `DefaultLockRepository` 实例存储的锁指定 `CLIENT_ID`。如果是这样,你可以将与 `DefaultLockRepository` 关联的 `id` 指定为构造函数参数。

从 5.1.8 版本开始,`JdbcLockRegistry` 可以通过 `idleBetweenTries` 进行配置,它是一个 `Duration`,用于在锁定记录插入/更新执行之间休眠。默认情况下,它为 `100` 毫秒,在某些环境中,非领导者过于频繁地污染数据源连接。

从 5.4 版本开始,引入了 `RenewableLockRegistry` 接口并将其添加到 `JdbcLockRegistry` 中。如果锁定的过程比锁的生存时间更长,则必须在锁定过程中调用 `renewLock()` 方法。这样可以大大减少生存时间,并且部署可以快速重新获得丢失的锁。

只有当前线程持有锁时,才能进行锁续期。

从 5.5.6 版本开始,`JdbcLockRegistry` 支持通过 `JdbcLockRegistry.setCacheCapacity()` 自动清理 `JdbcLockRegistry.locks` 中 JdbcLock 的缓存。有关更多信息,请参阅其 JavaDocs。

从 6.0 版本开始,可以为 `DefaultLockRepository` 提供一个 `PlatformTransactionManager`,而不是依赖于应用程序上下文中的主 bean。

从 6.1 版本开始,可以为 `DefaultLockRepository` 配置自定义的 `insert`、`update` 和 `renew` 查询。为此,公开了相应的 setter 和 getter 方法。例如,可以这样配置 PostgreSQL 提示的插入查询

lockRepository.setInsertQuery(lockRepository.getInsertQuery() + " ON CONFLICT DO NOTHING");

从 6.4 版本开始,`LockRepository.delete()` 方法返回移除分布式锁所有权的结果。并且如果锁的所有权已过期,`JdbcLockRegistry.JdbcLock.unlock()` 方法会抛出 `ConcurrentModificationException`。