在單機環境下的並行問題,我們可以使用相關鎖來解決;但是在叢集環境中,筆者測試通過Nginx
做的反向代理和負載均衡,請求的時候鎖會出現失效的問題。
原因:我們部署多個服務(存在多個tomcat
伺服器),每個tomcat
都有一個屬於自己的jvm
.每個鎖在同容器中有效,但是跨容器後就無法實現互斥效果。
引出分散式鎖:
一個最基本的分散式鎖需要滿足:
分散式鎖的實現:
SETNX
實現分散式鎖GitHub完整程式碼:https://github.com/xbhog/hm-dianping/tree/20230211-xbhog-redisCloud
鎖介面實現:20230211-xbhog-redisCloud
/**
* @author xbhog
* @describe:
* @date 2023/2/16
*/
public interface ILock {
boolean tryLock(Long timeOutSec);
void unLock();
}
加鎖解鎖實現類:
@Override
public boolean tryLock(Long timeOutSec) {
String threadId = ID_PREFIX + Thread.currentThread().getId();
Boolean isLock = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + keyName, threadId + "", timeOutSec, TimeUnit.SECONDS);
//防止拆箱引發空值異常
return Boolean.TRUE.equals(isLock);
}
@Override
public void unlock() {
//通過del刪除鎖
stringRedisTemplate.delete(KEY_PREFIX + name);
}
現在有兩個鎖,執行緒1獲取鎖時,由於業務的阻塞超時釋放了,這是執行緒2開始操作,獲取鎖,線上程2執行業務期間,執行緒1業務在一段時間內不阻塞且業務完成,這是開始執行釋放鎖的操作,但是這是鎖是執行緒2,由此造成鎖的誤刪問題;
正確流程:
解決的方式:
修改之前的分散式鎖實現,滿足:在獲取鎖時存入執行緒標示(可以用UUID表示) 在釋放鎖時先獲取鎖中的執行緒標示,判斷是否與當前執行緒標示一致
核心邏輯:在存入鎖時,放入自己執行緒的標識,在刪除鎖時,判斷當前這把鎖的標識是不是自己存入的,如果是,則進行刪除,如果不是,則不進行刪除。
處理流程:
程式碼實現:
private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";
@Override
public boolean tryLock(Long timeOutSec) {
String threadId = ID_PREFIX + Thread.currentThread().getId();
Boolean isLock = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + keyName, threadId + "", timeOutSec, TimeUnit.SECONDS);
//防止拆箱引發空值異常
return Boolean.TRUE.equals(isLock);
}
@Override
public void unLock() {
String threadId = ID_PREFIX + Thread.currentThread().getId();
//獲取當前分散式鎖中的value
String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + keyName);
//鎖相同則刪除
if(threadId.equals(id)){
stringRedisTemplate.delete(KEY_PREFIX + keyName);
}
}