JDBC 锁注册表

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

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

JdbcLockRegistry 基于 LockRepository 抽象,该抽象具有 DefaultLockRepository 实现。数据库模式脚本位于 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 配置自定义的 insertupdaterenew 查询。为此,公开了相应的 setter 和 getter。例如,PostgreSQL 提示的插入查询可以这样配置

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