分布式锁选型背后的架构设计思维【附源码】
1. 分布式鎖本質
提到分布式鎖,有很多實現,比如Redis分布式鎖、ZooKeeper分布式鎖、etcd分布式鎖等。但是選擇哪個更適合你的項目?在《基于CAP模型設計企業級真正高可用的分布式鎖》一文深入分析過分布式鎖的哲學本質,以及如何結合場景來選擇合適的分布式鎖。分析業務場景,得到業務本質,就是架構思維。思維最終是需要落地的,接下去分享一下對分布式鎖的思考和實踐。
鎖的本質是對共享資源的處理,表現很多,有以下作用:
業務協調
業務冪等(需配合業務代碼實現)
共享資源競爭
在單體應用時代表現為同步塊lock。隨著需求和業務量的增長,系統走向了分布式、微服務時代,多服務和多實例下的應用無法使用本地鎖進行控制資源共享。此時就出現了分布式鎖,分布式場景下對分布式鎖的要求如下:
強一致性
服務高可用、系統穩健
鎖自動續約、自動釋放
業務可重入
2. 分布式鎖存儲選型及場景
目前常見分布式鎖的實現有Redis、ZooKeeper、etcd等,各維度指標對比如下:
圖1?分布式鎖存儲模型對比一致性算法(CAP):在分布式場景下,CAP理論是很多架構設計的指導思想。CAP思想下有兩個分支CP與AP;CP模型不管什么情況下,都要求各服務之間的數據一致;AP模型高可用下的數據最終一致性。雖然鎖原本要求強一致性CP模型,但AP模型分布式鎖的使用取決于業務場景對臟數據的最大容忍度,比如SNS場景,就可以使用AP模型分布式鎖,從而在性能上有很大的優勢。CP模型仍然保持原有的一致性要求,保證了業務資源串行競爭,更加適合于金融交易場景的強數據要求。Redis自身無一致性算法來保證多節點的數據一致性,所以是AP模型;ZooKeeper、etcd都有一致性算法,都是CP模式。
高可用:Redis是一個K-V存儲,使用主從模式進行集群,Redis Cluster 底層也是主從模式的組合,性能高,保證了高可用。ZooKeeper是Tree的數據結構,節點要求N + 1,N必須大于2,通過ZAB選舉保障主的可用。etcd是一個K-V存儲,節點要求N + 1,N必須大于2,通過Raft選舉保障主的高可用。
3. 分布式鎖接口設計
根據需求,設計出鎖接口,首先鎖的基本方法如下:
/*** @方法名稱 lock* @功能描述 <pre>獲取鎖</pre>* @param ttl 鎖過期時間,單位毫秒* @return true-獲取鎖,false-為獲得鎖* @throws RuntimeException 操作鎖失敗,需要業務判斷是否重試*/boolean lock(int ttl) throws RuntimeException;第二,分布式鎖可以處理業務冪等,可用作為消息去重等場景,設計競爭鎖方法如下:
?/*** @方法名稱 acquire* @功能描述 <pre>競爭鎖,并自動續租</pre>* @param ttl 鎖過期時間,單位毫秒* @return true-獲取鎖,false-為獲得鎖* @throws RuntimeException 操作鎖失敗,需要業務判斷是否重試*/default boolean acquire(int ttl)throws RuntimeException {if (lock(ttl)) {logger.debug(MSG_LOCK, getName());startHeartBeatThread();return true;}return false;}第三,作為鎖的基本要求,業務的串行執行,設計等待鎖方法如下:
???/*** @方法名稱acquireOrWait* @功能描述 <pre>競爭鎖或等待鎖</pre>* @param ttl 鎖過期時間,單位毫秒* @param waitTime 等待時間,單位毫秒* @return true-獲取鎖,false-為獲得鎖* @throws InterruptedException* @throws RuntimeException 操作鎖失敗,需要業務判斷是否重試*/default boolean acquireOrWait(int ttl, int waitTime)throws InterruptedException,RuntimeException {while (!lock(ttl)) {waitTime =waitTime - ttl / 2;Thread.sleep(ttl / 2);if (waitTime<= 0) {logger.debug(MSG_LOCK_TIMEOUT, getName());return false;}}startHeartBeatThread();return true;}第四,分布式場景,鎖需要自動續租方法,保障鎖內業務完整執行,如下:
/*** @方法名稱startHeartBeatThread* @功能描述 <pre>續租心跳</pre>*/void startHeartBeatThread();?第五,鎖需要自動釋放,為保證使用簡單,所以重寫Closeable接口:/*** @方法名稱 close* @功能描述 <pre>釋放鎖</pre>*/@Overridevoid close();/*** @方法名稱 release* @功能描述 <pre>釋放鎖</pre>*/default void release() {close();}4. AP模型的Redis分布式鎖實現
圖2?AP模型的Redis分布式鎖實現?
?
5. CP模型的etcd分布式鎖實現
etcd有V2和V3兩種接口:V2接口可以使用http直接訪問,天然客戶端物理解耦,但需要自動續租保證鎖的完整性。V3接口默認grpc形式,是長鏈接機制,天然續租,但grpc有客戶端依賴要求。可以根據場景要求,適度選擇合適版本接口。
鎖參數有:
prevExits:檢查是否存在,true:新增,false:更新;
prevIndex:檢查上一個的key,既操作返回的uuid;
prevValue:檢查上一個的值;
Linux curl鎖操作:
取鎖:curl http://ip:port/v2/keys/鎖名 -XPUT -d ttl=10 -d prevExits=false -d value=鎖值
續租:curl http://ip:port/v2/keys/鎖名?prevValue=鎖值 -XPUT -d ttl=3 -dprevExits=true -d refresh=true
釋放鎖:curl ?http://ip:port/v2/keys/鎖名?prevValue=鎖值 -XDELETE
6. CP模型的etcd分布式鎖實現源碼
全部源代碼請訪問以下鏈接:
https://github.com/linhuaichuan/ecp-uid/tree/master/src/test/java/com/myzmds/ecp/core/standard/distributed/lock
作者簡介:
林淮川 畢業于西安交通大學,現任大樹金融架構師,技術委員會委員;前大樹金融供應鏈金融技術總監;前天陽宏業交易事業部技術主管。5年互聯網金融行業業務經驗。多次主導金融服務平臺的設計、策劃、實施與交付。擁有豐富的大型軟件平臺構架設計經驗,以及供應鏈金融業務經驗。
總結
以上是生活随笔為你收集整理的分布式锁选型背后的架构设计思维【附源码】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 公司的API接口被刷了,那是因为你没这样
- 下一篇: 你写的 Java 代码是如何一步步输出结