82zookeeper 分佈式鎖(二)避免羊羣效應 分佈式解決方案: 分佈式鎖有哪些應用場景

分佈式解決方案:

分佈式鎖常見問題:

1,Zookeeper如何實現分佈式鎖;

1,重試策略;
2,超時策略;
3,續命設計,續命如何避免死鎖問題。
4,性能優化。
5,高可用
6,公平性。

2, 業務超時,一直不釋放鎖如何處理;

可以採用續命設計,如果業務超時(),續命多次(3次)如果還沒有釋放鎖的情況下,則認爲超時
就應該:
1,主動釋放鎖;
2,事務回滾
3,主動停止線程
4,移除key
代碼思路: 記錄每次獲取鎖的線程,鎖的信息 事務信息。
業務如果超時:定義: 當前線程獲取到鎖之後,在規定的時間內需要釋放鎖,避免其他的jvm一直阻塞等待。
可以採用續命代碼設計: 續命多次如果業務還是沒有執行完畢的情況下,則認爲該鎖超時,應主動釋放該鎖,防止其他jvm一直阻塞等待。
續命代碼設計:
1,主動釋放鎖
2,回滾當前業務邏輯
3,主動停止該線程
4,移除監聽;
代碼實現

/**
 * 續命設計
 */
/**
 * 每隔2s執行定時任務
 */
@Scheduled(cron = "0/2 * * * * *")
@Transactional
public synchronized void taskService() {
    try {
        // 拉取數據庫數據 調用第三方短信接口發送短信
        // 獲取鎖
        zookeeperTemplateLock.getLock();
        //##記錄鎖的開始執行業務邏輯信息
        TransactionStatus begin = translationUtils.begin();
        String lockId = UUID.randomUUID().toString();
        lockInfoMap.put(UUID.randomUUID().toString(), new LockInfo(lockId, Thread.currentThread(), STATE_START, zookeeperTemplateLock
                , translationUtils, begin));
        log.info("[{}]正在調用阿里雲發送短信", serverPort);
        userMapper.insert("mayikt" + System.currentTimeMillis(), 22);
        // 模擬業務超時
        Thread.sleep(500000);
        translationUtils.rollback(begin);
        // 釋放鎖
        lockInfoMap.put(UUID.randomUUID().toString(), new LockInfo(lockId, Thread.currentThread(), STATE_STOP,
                zookeeperTemplateLock, translationUtils, begin));
        zookeeperTemplateLock.unLock();

    } catch (Exception e) {
        log.error("[e:{}]", e);
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}



public MayiktTask() {
    scheduledExecutorService.scheduleAtFixedRate(new DetectionAlgorithm(), 0, 5, TimeUnit.SECONDS);
}

/**
 * 檢測死鎖的問題
 */
class DetectionAlgorithm implements Runnable {

    @Override
    public void run() {
        lockInfoMap.forEach((k, lockInfo) -> {
            if (STATE_START.equals(lockInfo.getState())) {
                // 主動釋放session連接
                lockInfo.getLock().unLock();
                // 事務回滾
                TransactionStatus transactionStatus = lockInfo.getTransactionStatus();
                lockInfo.getTranslationUtils().rollback(transactionStatus);
                log.info("[serverPort:{},lockId:{}]", serverPort, lockInfo + "事務已經回滾...");
                // 主動停止線程
                lockInfo.getLockThread().interrupt();
                // 移除
                lockInfoMap.remove(k);
            }
        });
    }
}
//##創建定時任務線程池
private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

3,分佈式鎖如何避免羊羣效應問題?

什麼是羊羣效應:
當jvm釋放鎖的時候,會喚醒正在等待的jvm從新進入到獲取鎖的狀態。
如果正在阻塞的等待獲取鎖的jvm,如果有幾十個或者幾百個,上千個的情況下zkServer端喚醒所有正在等待的jvm,從新進入到獲取鎖的狀態,喚醒的成本是非常高,有可能會造成我們的zkserver端阻塞。

基於zk實現分佈式鎖的2種實現方案
1,多個jvm同時在zk上創建一個相同的臨時節點,誰能夠創建成功,誰就可以拿到這把鎖。
存在羊羣效應的bug。
2,基於臨時順序編號節點實現,多個jvm同時創建一個臨時順序編號節點,如果當前jvm創建的臨時順序編號節點是最小的情況下,則表示獲取鎖成功。如果不是最小的情況下,則表示獲取鎖失敗,就會進入到阻塞狀態:當前的jvm訂閱到我們的上一個節點。;
核心思想: 當我們的jvm釋放鎖的時候,不會通知一羣正在等待獲取鎖的jvm,而是會通知一個jvm獲取鎖,效率變高,對我們的zkserver端減去事件通知的壓力。
多個jvm槍鎖,最終只會有一個jvm能夠搶成功。
類似於: juc併發編程中 公平鎖實現原理。
僞代碼實現:
1,多個jvm同時在zk/localpath 創建一個臨時編號節點
2,每個jvm都能夠創建到自己獨立的臨時順序編號節點。
3,舉個例子:
jvm01/localpath01
jvm02/localpath02
jvm03 /localpath03
獲取鎖的流程:
jvm01--
1,獲取當前jvm 創建的臨時編號節點localpath01
2,查詢到localpath下所有的子節點,實現排序 查找到最小的
3,最小的臨時順序編號節點:localPath01
localPath01==localpath01 如果是最小的節點情況下,則表示獲取鎖成功。
Jvm02--
1,查詢當前jvm創建的臨時節點localPath02
2,查詢到localpath下所有的子節點,實現排序,查找到最小的
3,最下的臨時順序 編號節點:localPath01
4,localPath02 !=localpath01
5,如果當前自己創建的臨時順序編號節點不是最小的情況下,則會直接阻塞
6.Jvm02訂閱到localPath01該臨時順序編號節點:
喚醒以後:
zk服務器端,當localpath01節點刪除之後,會通知給jvm02,jvm02從組賽狀態到運行狀態。
1,獲取當前的jvm創建的臨時編號節點localpath02
2,查詢到localpath下所有的子節點,實現排序,查找到最小的
3,最小的臨時順序編號節點:localpath02
4,localpath02==localpath02 屬於最小節點
5,獲取鎖成功。

羊羣效應發生的原因:
如果多個客戶端同時訂閱同一個節點的情況下,當該節點失效的時候,有可能會通知給所有的客戶端,如果客戶端有幾千或者幾萬個的情況下,這時會導致zk阻塞效率且非常差。本身我們最終只有一個jvm能夠拿到鎖,也沒有必要通知給所有的客戶端。
基於zk實現分佈式鎖如何解決羊羣效應問題:


1,多個jvm首先會先創建一個臨時順序節點,在通過排序的方式找到最小的節點路徑
2,如果當前節點爲最小的節點的情況下,則表示獲取鎖。
3,如果當前節點不是爲最小的節點的情況下,訂閱上一個節點。
串行化
效果優點類似於:公平鎖:

4,如何提高分佈式鎖的效率問題?

避免羊羣效應;

5,Redis與Zookeeper 實現分佈式鎖區別?

6,分佈式鎖框架Curator源碼解讀

7,分佈式框架Redisson源碼解讀

8 如何保證分佈式鎖高可用性問題。

分佈式鎖有哪些應用場景

1,集羣環境中保證定時任務執行的冪等性問題,基於zk案例;
冪等性:執行結果保證唯一不能夠重複。
當我們的定時任務服務集羣的情況下,有可能會同時重複執行定時任務。
解決思路: 多個jvm集羣的定時任務,在觸發的時候,獲取分佈式鎖,如果能夠獲取分佈式鎖的jvm,就能夠執行定時任務,沒有獲取到分佈式鎖的jvm就不能夠執行定時任務。



2,用戶下單庫存超賣問題,防止超賣Redis解決。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章