前言:這幾天忙合作方的項目,就在剛剛如期上線了, 才得以得空,閑下來,和大家吹吹牛,討論討論技術,吹吹牛逼,打發著這閑淡的時光。話不多說 ?咋們直接進入正題。討論一下ZK的分布式鎖以及在生產環境中如何優化我們ZK的服務配置。
在分布式服務下,要想利用ZK實現一個分布式鎖,可利用zk的節點特殊性,來實現我們的分布式鎖。上一章 我們已經介紹了ZK的節點基本知識。我們可以通過zk的臨時節點實現分布式鎖的機制
通過以下方式來實現:
1 利用節點名稱的唯一性來實現共享鎖
ZooKeeper機制規定:同一個目錄下只能有一個唯一的文件名。例如:我們在Zookeeper目錄/test目錄下創建,兩個客戶端創建一個名為Lock節點,只有一個能夠成功。即只有一個服務能創建成功。即創建成功的服務獲取分布式鎖,解鎖時 ,刪除相應的節點即可,其余客戶端再次進入競爭創建節點,直到所有的客戶端都能獲得鎖。完成自己的業務邏輯。
2 利用臨時順序節點實現共享鎖的一般做法
Zookeeper中有一種節點叫做順序節點,故名思議,假如我們在/lock/目錄下創建節3個點,ZooKeeper集群會按照提起創建的順序來創建節點,節點分別為/test/lock/0000000001、/test/lock/0000000002、/test/lock/0000000003。
ZK的臨時節點特性 ?當客戶端與ZK集群斷開連接,則臨時節點自動被刪除。
利用上面這兩個特性,我們來看下獲取實現分布式鎖的基本邏輯:
客戶端調用create()方法創建名為“test/lock-*”的節點,需要注意的是,這里節點的創建類型需要設置為EPHEMERAL_SEQUENTIAL。
客戶端調用getChildren(“lock-*”)方法來獲取所有已經創建的子節點,同時在這個節點上注冊上子節點變更通知的Watcher。
客戶端獲取到所有子節點path之后,循環子節點 發現創建的節點是所有節點中序號最小的,那么就認為這個客戶端獲得了鎖。如果發現循環過程中發現自己創建的節點不是最小的,說明自己還沒有獲取到鎖,就開始等待,直到下次子節點變更通知的時候,再進行子節點的獲取,判斷是否獲取鎖。
我知道的就這兩種方式,可能有其他的方式,網上有人說,還有其他的方式,具體的我沒有深入,也就不知道了,如果你深入了解的話,歡迎來信交流。
而在實際的項目中,我們通常會采取第一種方式,因為第二種方式有一個不好的地方:
即在獲取所有的子點,判斷自己創建的節點是否已經是序號最小的節點”,這個過程,在整個分布式鎖的競爭過程中,大量重復運行,并且絕大多數的運行結果都是判斷出自己并非是序號最小的節點,從而繼續等待下一次通知——這個顯然看起來不怎么科學。客戶端無端的接受到過多的和自己不相關的事件通知,這如果在集群規模大的時候,會對Server造成很大的性能影響,并且如果一旦同一時間有多個節點的客戶端斷開連接,這個時候,服務器就會像其余客戶端發送大量的事件通知 這是一個不友好的方式。所以我們一般利用ZK來實現我們分布式鎖的時候 通常會采用第一種方式來 實現。
像我們做游戲sdk 實時語音的,經常對接游戲Cp 用戶量對于我們來說是很大,玩家通過我們的語音服務在游戲交流。那么場景來了:
? ? 不同的游戲方 對于我們來說 是不同的應用 用戶登入我們實時語音時 我們是根據應用隨機分配服務器的,那么在并發量大的時候 肯定在分配的時候 存在競爭,而我們的服務 又是分布式的 此時分布式鎖的場景就出現了。我們利用第一種方式來實現我們的分布式鎖和自己的業務 。
由于我們是用curator 來實現我們分布式鎖,而curator實現分布式鎖有好幾種鎖的方式:
1 共享鎖? InterProcessMutex
2 讀寫鎖? InterProcessReadWriteLock
3 共享信號量 InterProcessSemaphoreV2
大概就這么幾種吧? 而我們的實際的項目中用的是InterProcessMutex來實現分布式鎖
此鎖屬于可重入式鎖,當一個客戶端獲取到lock鎖之后,可以重復調用acquire()而不會發生阻塞。基于InterProcessSemaphoreMutex實現的分布式的分布式鎖是不可重入的,當一個客戶端獲取到lock鎖之后,再次調用acquire方法獲取鎖時會發生阻塞。基于InterProcessReadWriteLock實現的分布式鎖里邊包含了讀鎖與寫鎖,其中讀鎖與讀鎖互斥,讀鎖與寫鎖互斥,讀鎖與讀鎖不互斥。所以我們就選擇了InterProcessMutex 來實現吧。
在分配服務的時候 實現分布式鎖:
釋放鎖:
獲取鎖:
其他兩種鎖的實現方式如下:
InterProcessReadWriteLock簡單的一個實例:
InterProcessSemaphoreV2 簡單的一個實例:
關于ZK的分布式鎖大概就介紹到這里吧 我對ZK的分布式鎖也就了解的這么多了,如果你比我更深入的話 歡迎私信 我等渣渣向大神學習。
zk啟動的時候 我們可以優化我們的配置 比如以下幾種:
1 如果zk jvm內存不夠的時候 我們要適當的增加:
更改{ZK_HOME}/bin/zkServer.sh,大約在109-110行。
nohup $JAVA “-Dzookeeper.log.dir=${ZOO_LOG_DIR}” “-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}” \
-cp “$CLASSPATH” $JVMFLAGS $ZOOMAIN “$ZOOCFG” > “$_ZOO_DAEMON_OUT” 2>&1 < /dev/null &
改為:
nohup $JAVA “-Xmx1G -Xms1G -Dzookeeper.log.dir=${ZOO_LOG_DIR}”…
即可增加內存。
2 zoo.cfg 配置文件 ?是zk的集群基本配置文件 ?調整里面的參數可以很好的保持我們的集群服務穩定。所以大家要對里面的配置熟悉。如下:
1.tickTime:CS通信心跳數
Zookeeper 服務器之間或客戶端與服務器之間維持心跳的時間間隔,也就是每個 tickTime 時間就會發送一個心跳。tickTime以毫秒為單位
tickTime=2000
2.initLimit:LF初始通信時限
集群中的follower服務器(F)與leader服務器(L)之間初始連接時能容忍的最多心跳數(tickTime的數量)
initLimit=10
3.syncLimit:LF同步通信時限
集群中的follower服務器與leader服務器之間請求和應答之間能容忍的最多心跳數(tickTime的數量)。
syncLimit=5
4.dataDir:數據文件目錄
Zookeeper保存數據的目錄,默認情況下,Zookeeper將寫數據的日志文件也保存在這個目錄里。
dataDir=E:\\comany\\zookeeper\\zkdata\\data1
5.dataLogDir:日志文件目錄
Zookeeper保存日志文件的目錄。
dataLogDir=E:\\comany\\zookeeper\\zkdata\\logs1
6.clientPort:客戶端連接端口
客戶端連接 Zookeeper 服務器的端口,Zookeeper 會監聽這個端口,接受客戶端的訪問請求。
clientPort=2181
7.服務器名稱與地址:集群信息(服務器編號,服務器地址,LF通信端口,選舉端口)
這個配置項的書寫格式比較特殊,規則如下:
server.1=127.0.0.1:2287:3387
#server.2=127.0.0.1:2288:3388
#server.3=127.0.0.1:2289:3389
尾記:今天就給大家介紹到這些了吧,我所知道的ZK的分布式鎖和服務優化也就這些了,只怪自己太渣,沒辦法,只能講到這個程度,時間如白駒過隙,一晃之間就到了9月,一年也就快過完了,自己也越來越年長,自己還是一如既往的渣渣,危機感也越來越重,愿未來更要自我約束,自我鞭策,自我學習。
時間不早了 該回去了? 晚上在產業園吃了碗面 完全不管飽,身體要緊,回去補點狗糧,我是小志碼字,一個簡單碼代碼的小人物。如果想了解這個項目和代碼? 加我微信? 微信號:2B青年? 歡迎交流 相互學習。
補一張公司加班狗的圖照: