JDBC 锁注册表

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

当锁由本地线程释放时,另一个本地线程通常可以立即获取该锁。 如果锁是由使用不同注册表实例的线程释放的,则获取该锁可能需要长达 100ms。spring-doc.cadn.net.cn

JdbcLockRegistry 基于 LockRepository 抽象,该抽象具有 DefaultLockRepository 实现。 数据库架构脚本位于 org.springframework.integration.jdbc 包中,该包针对不同关系型数据库管理系统(RDBMS)厂商进行了划分。 例如,以下代码清单展示了用于锁表的 H2 DDL:spring-doc.cadn.net.cn

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

INT_ 可根据目标数据库设计需求进行修改。 因此,您必须在 DefaultLockRepository bean 定义上使用 prefix 属性。spring-doc.cadn.net.cn

有时,一个应用程序会进入无法释放分布式锁并从数据库中移除特定记录的状态。 为此,其他应用程序可以在下一次加锁调用时使此类死锁过期。 timeToLive(TTL)选项在DefaultLockRepository上提供,正是用于此目的。 对于存储在给定DefaultLockRepository实例中的锁,您可能还希望指定CLIENT_ID。 如果是这样,您可以将id作为构造函数参数与DefaultLockRepository关联起来。spring-doc.cadn.net.cn

从版本 5.1.8 开始,JdbcLockRegistry 可以使用 idleBetweenTries 进行配置——这是一个在锁定记录插入/更新执行之间睡眠的 Duration。 默认情况下,它是 100 毫秒,并且在某些环境中,非领导者会过于频繁地通过数据源污染连接。spring-doc.cadn.net.cn

从 5.4 版本开始,RenewableLockRegistry 接口已被引入并添加到 JdbcLockRegistry 中。 在锁定进程可能超过锁的生存时间的情况下,必须调用 renewLock() 方法,且该调用需在锁定进程中执行。 因此,生存时间可以大幅缩短,部署也能更快地重新获取丢失的锁。spring-doc.cadn.net.cn

锁的续期操作仅当锁由当前线程持有时方可执行。

从版本 5.5.6 开始,JdbcLockRegistry 支持通过 JdbcLockRegistry.setCacheCapacity() 自动清理 JdbcLock 的缓存。JdbcLockRegistry.locks。 有关更多信息,请参阅其 Javadocs。spring-doc.cadn.net.cn

从版本 6.0 开始,DefaultLockRepository 可以改用 PlatformTransactionManager 提供,而无需依赖应用程序上下文中的主 Bean。spring-doc.cadn.net.cn

从版本 6.1 开始,DefaultLockRepository 可以配置为自定义 insert、update 和 renew 查询。 为此,相应的 setter 和 getter 方法已被暴露。 例如,PostgreSQL 提示的插入查询可以这样配置:spring-doc.cadn.net.cn

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

从版本 6.4 开始,LockRepository.delete() 方法返回移除分布式锁所有权的操作结果。 而 JdbcLockRegistry.JdbcLock.unlock() 方法在锁的所有权已过期时抛出 ConcurrentModificationException 异常。spring-doc.cadn.net.cn

从版本 7.0 开始,JdbcLock 实现了 DistributedLock 接口,以支持锁状态数据的自定义生存时间(TTL)功能。 现在可以使用 lock(Duration ttl) 或 tryLock(long time, TimeUnit unit, Duration ttl) 方法获取 JdbcLock,并指定生存时间(TTL)值。 JdbcLockRegistry 现在提供了新的 renewLock(Object lockKey, Duration ttl) 方法,允许您使用自定义生存时间值续期锁。 所有存储在同一个 JdbcLockRegistry 中的 JdbcLock 实例的默认生存时间,现在可以通过新的构造函数 JdbcLockRegistry(LockRepository client, Duration expireAfter) 进行设置。 LockRepository 和 DefaultLockRepository 的 API 也经过修改以支持该功能。spring-doc.cadn.net.cn

spring-doc.cadn.net.cn

spring-doc.cadn.net.cn

如果您已经在使用 JdbcLockRegistry 或 DefaultLockRepository 的早期版本,请在升级到此版本之前执行必要的 DDL 以修改 INT_LOCK 表。spring-doc.cadn.net.cn

这是向锁表添加新列的 Postgres DDL 示例:spring-doc.cadn.net.cn

ALTER TABLE INT_LOCK ADD EXPIRED_AFTER TIMESTAMP NOT NULL;