小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

分布式鎖實現(xiàn)方案(REDIS,ZOOKEEPER,TAIR)

 沙門空海 2018-02-23

 Zookeeper

1、原生ZK方案

Zookeeper中有一種節(jié)點叫做順序節(jié)點,假如我們在/lock/目錄下創(chuàng)建節(jié)3個點,ZooKeeper集群會按照提起創(chuàng)建的順序來創(chuàng)建節(jié)點,節(jié)點分別為/lock/0000000001、/lock/0000000002、/lock/0000000003。

ZooKeeper中還有一種名為臨時節(jié)點的節(jié)點,臨時節(jié)點由某個客戶端創(chuàng)建,當客戶端與ZooKeeper集群斷開連接,則開節(jié)點自動被刪除。

EPHEMERAL_SEQUENTIAL為臨時順序節(jié)點

實現(xiàn)分布式鎖的基本邏輯:

  • 客戶端調(diào)用create()方法創(chuàng)建名為“locknode/guid-lock-”的節(jié)點,需要注意的是,這里節(jié)點的創(chuàng)建類型需要設(shè)置為EPHEMERAL_SEQUENTIAL。

  • 客戶端調(diào)用getChildren(“locknode”)方法來獲取所有已經(jīng)創(chuàng)建的子節(jié)點。

  • 客戶端獲取到所有子節(jié)點path之后,如果發(fā)現(xiàn)自己在步驟1中創(chuàng)建的節(jié)點是所有節(jié)點中序號最小的,那么就認為這個客戶端獲得了鎖。

  • 如果創(chuàng)建的節(jié)點不是所有節(jié)點中需要最小的,那么則監(jiān)視比自己創(chuàng)建節(jié)點的序列號小的最大的節(jié)點,進入等待。直到下次監(jiān)視的子節(jié)點變更的時候,再進行子節(jié)點的獲取,判斷是否獲取鎖。

釋放鎖的過程相對比較簡單,就是刪除自己創(chuàng)建的那個子節(jié)點即可。

以下是流程圖:


讀寫鎖:讀寫鎖的實現(xiàn)與互斥鎖類似,不同的地方在于創(chuàng)建自節(jié)點時讀鎖和寫鎖要區(qū)分類型。例如讀鎖的前綴可以設(shè)置為read,寫鎖的前綴可以設(shè)置為write。創(chuàng)建讀鎖的時候,檢查是否有編號小于自己的寫鎖存在,若存在則對編號剛好小于自己的寫鎖節(jié)點進行監(jiān)聽。創(chuàng)建寫鎖時,檢查創(chuàng)建的節(jié)點編號是否為最小,如不是最小,則需要對編號剛好小于自己的節(jié)點進行監(jiān)聽(此時不區(qū)分讀鎖和寫鎖)

2、Curator方案

封裝了zk的客戶端,其分布式實現(xiàn)方式和上面的基本相同。同時還提供了不同的鎖類型:

可重入鎖:實現(xiàn)類為InterProcessMutex,將線程對象,節(jié)點,鎖對象相關(guān)聯(lián)。InterProcessMutex內(nèi)部維護了一個使用線程為key,{thread,path}為值的map,所以對不同的線程和請求加鎖的節(jié)點進行一一對應(yīng)。提供方法acquire 和 release。

不可重入鎖:實現(xiàn)類為InterProcessSemaphoreMutex,類似InterProcessMutex,只是沒有維護線程的map。

可重入讀寫鎖:類似JDK的ReentrantReadWriteLock.一個讀寫鎖管理一對相關(guān)的鎖。 主要由兩個類實現(xiàn):

  • InterProcessReadWriteLock

  • InterProcessLock

使用時首先創(chuàng)建一個InterProcessReadWriteLock實例,然后再根據(jù)你的需求得到讀鎖或者寫鎖, 讀寫鎖的類型是InterProcessLock。

讀寫鎖的實現(xiàn)與互斥鎖類似,不同的地方在于創(chuàng)建自節(jié)點時讀鎖和寫鎖要區(qū)分類型。例如讀鎖的前綴可以設(shè)置為read,寫鎖的前綴可以設(shè)置為write。創(chuàng)建讀鎖的時候,檢查是否有編號小于自己的寫鎖存在,若存在則對編號剛好小于自己的寫鎖節(jié)點進行監(jiān)聽。創(chuàng)建寫鎖時,檢查創(chuàng)建的節(jié)點編號是否為最小,如不是最小,則需要對編號剛好小于自己的節(jié)點進行監(jiān)聽(此時不區(qū)分讀鎖和寫鎖)

還有信號量和多鎖對象。

3、menagerie方案

menagerie基于Zookeeper實現(xiàn)了java.util.concurrent包的一個分布式版本。這個封裝是更大粒度上對各種分布式一致性使用場景的抽象。其中最基礎(chǔ)和常用的是一個分布式鎖的實現(xiàn):
org.menagerie.locks.ReentrantZkLock,通過ZooKeeper的全局有序的特性和EPHEMERAL_SEQUENTIAL類型znode的支持,實現(xiàn)了分布式鎖。

Redis

最常見互斥鎖方案:

Redis的SETNX(即SET if Not eXists)GETSET先寫新值,返回舊值,原子性操作,可以用于分辨是不是首次操作)可以用于分布式鎖:
  1. C3發(fā)送SETNX lock.{orderid} 想要獲得鎖,由于C0還持有鎖,所以Redis返回給C3一個0,

  2. C3發(fā)送GET lock.{orderid} 以檢查鎖是否超時了,如果沒超時,則等待或重試。

  3. 反之,如果已超時,C3通過下面的操作來嘗試獲得鎖:
    GETSET lock.{orderid} <current Unix time + lock timeout + 1>

  4. 通過GETSET,C3拿到的時間戳如果仍然是超時的,那就說明,C3如愿以償拿到鎖了。

  5. 如果在C3之前,有個叫C4的客戶端比C3快一步執(zhí)行了上面的操作,那么C3拿到的時間戳是個未超時的值,這時,C3沒有如期獲得鎖,需要再次等待或重試。留意一下,盡管C3沒拿到鎖,但它改寫了C4設(shè)置的鎖的超時值,不過這一點非常微小的誤差帶來的影響可以忽略不計。

jeffkit的偽碼參考:
  1. # get lock
  2. lock = 0
  3. while lock != 1:
  4.     timestamp = current Unix time + lock timeout + 1
  5.     lock = SETNX lock.orderid timestamp
  6.     if lock == 1 or (now() > (GET lock.orderid) and now() > (GETSET lock.orderid timestamp)):
  7.         break
  8.     else:
  9.         sleep(10ms)
  10. do_your_job()
  11. # release lock
  12. if now() < GET lock.orderid:
  13.     DEL lock.orderid

Tair

設(shè)計思路和Medis類似,但實現(xiàn)略有不同。

美團維護的Tair中增加了expireLock和expireUnlock接口,通過鎖狀態(tài)和過期時間戳來共同判斷鎖是否存在:只有鎖已經(jīng)存在且沒有過期的狀態(tài)才判定為有鎖狀態(tài)。在有鎖狀態(tài)下,不能加鎖,能通過大于過期時間的時間戳進行解鎖;在無鎖狀態(tài)下,可以加鎖,加鎖成功會返回過期時間戳,用于解鎖使用。重要的是,expireLock的原子性可以保證加鎖和解鎖時不會因為線程搶占引起錯誤。

不可重入鎖:在加鎖時調(diào)用expireLock,解鎖時調(diào)用expireUnlock接口。傳入的參數(shù)為過期時間或者過期時間戳??梢苑乐巩斁€程拿到鎖之后阻塞或者宕機,鎖可以在過期之后釋放出來。同時可以滿足解鎖動作安全,當自己的鎖過期時不會誤刪別人的鎖。

可重入鎖:類似不可重入鎖,維護類似zk的一個線程數(shù)和鎖名的map。

可重入讀寫鎖:

讀線程:先用當前時間進行一次解鎖expireUnlock,如果能解開則說明沒有線程在寫,可以進行讀操作,同時incr,將計數(shù)器加1;完成讀之后進行decr。

寫線程:getCount讀取計數(shù)器,如果為0,則說明沒有線程在讀,否則則需要等待;再expireLock,如果成功說明獲取到了寫鎖,否則則說明已經(jīng)有線程在寫了;完成寫之后進行解鎖expireUnlock

缺陷:均有兩步操作,但無法保證原子性。

    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多