分散式鎖
在許多情況下,對某個上下文(甚至是單個訊息)執行的操作必須以獨佔方式進行。一個例子是聚合器元件,我們必須檢查當前訊息的訊息組狀態,以確定是釋放組還是僅將該訊息新增到將來考慮。為此,Java 提供了一個帶有 java.util.concurrent.locks.Lock 實現的 API。然而,當應用程式是分散式的和/或在叢集中執行時,問題變得更加複雜。在這種情況下,鎖定具有挑戰性,需要一些共享狀態及其特定的方法來實現排他性要求。
Spring Integration 提供了一個 LockRegistry 抽象,其記憶體中的 DefaultLockRegistry 實現基於 ReentrantLock API。LockRegistry 的 obtain(Object) 方法需要一個用於特定上下文的 lock key。例如,聚合器使用 correlationKey 來鎖定其組周圍的操作。這樣,不同的鎖可以併發使用。此 obtain(Object) 方法返回一個 java.util.concurrent.locks.Lock 例項(取決於 LockRegistry 實現),因此,其餘邏輯與標準 Java 併發演算法相同。
從版本 6.2 開始,LockRegistry 提供了 executeLocked() API(此介面中的 default 方法)以在鎖定狀態下執行某些任務。此 API 的行為類似於眾所周知的 JdbcTemplate、JmsTemplate 或 RestTemplate。以下示例演示了此 API 的用法
LockRegistry registry = new DefaultLockRegistry();
...
registry.executeLocked("someLockKey", () -> someExclusiveResourceCall());
該方法重新丟擲任務呼叫中的異常,如果 Lock 被中斷,則丟擲 InterruptedException。此外,帶有 Duration 的變體在 lock.tryLock() 返回 false 時丟擲 java.util.concurrent.TimeoutException。
Spring Integration 為分散式鎖提供了以下 LockRegistry 實現
Spring Cloud AWS 還提供了 DynamoDbLockRegistry。
從版本 7.0 開始,引入了 DistributedLock 介面,提供了新方法 lock(Duration ttl) 和 tryLock(long time, TimeUnit unit, Duration ttl),用於獲取具有自定義生存時間 (TTL) 的鎖。JdbcLock 和 RedisLock 都實現了 DistributedLock 介面以支援自定義生存時間功能。LockRegistry<L extends Lock> 現在是擴充套件 Lock 的型別的通用介面。RenewableLockRegistry 介面現在提供了新的 renewLock(Object lockKey, Duration ttl) 方法,允許您使用自定義生存時間值續訂鎖。JdbcLockRegistry 和 RedisLockRegistry 都使用型別引數 DistributedLock 實現了 LockRegistry 和 RenewableLockRegistry 介面。
以下是如何從登錄檔中獲取 DistributedLock 並以特定生存時間值獲取它的示例
DistributedLock lock = registry.obtain("foo");
Duration timeToLive = Duration.ofMillis(500);
if(lock.tryLock(100, TimeUnit.MILLISECONDS, timeToLive)){
try {
// do something
} catch (Exception e) {
// handle exception
} finally{
lock. unlock();
}
}