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");

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