前言:這幾天忙合作方的項(xiàng)目,就在剛剛?cè)缙谏暇€了矛渴, 才得以得空椎扬,閑下來(lái),和大家吹吹牛具温,討論討論技術(shù)蚕涤,吹吹牛逼,打發(fā)著這閑淡的時(shí)光桂躏。話不多說(shuō) ?咋們直接進(jìn)入正題钻趋。討論一下ZK的分布式鎖以及在生產(chǎn)環(huán)境中如何優(yōu)化我們ZK的服務(wù)配置。
在分布式服務(wù)下剂习,要想利用ZK實(shí)現(xiàn)一個(gè)分布式鎖蛮位,可利用zk的節(jié)點(diǎn)特殊性,來(lái)實(shí)現(xiàn)我們的分布式鎖鳞绕。上一章 我們已經(jīng)介紹了ZK的節(jié)點(diǎn)基本知識(shí)失仁。我們可以通過(guò)zk的臨時(shí)節(jié)點(diǎn)實(shí)現(xiàn)分布式鎖的機(jī)制
通過(guò)以下方式來(lái)實(shí)現(xiàn):
1 利用節(jié)點(diǎn)名稱的唯一性來(lái)實(shí)現(xiàn)共享鎖
ZooKeeper機(jī)制規(guī)定:同一個(gè)目錄下只能有一個(gè)唯一的文件名。例如:我們?cè)赯ookeeper目錄/test目錄下創(chuàng)建们何,兩個(gè)客戶端創(chuàng)建一個(gè)名為L(zhǎng)ock節(jié)點(diǎn)萄焦,只有一個(gè)能夠成功。即只有一個(gè)服務(wù)能創(chuàng)建成功冤竹。即創(chuàng)建成功的服務(wù)獲取分布式鎖拂封,解鎖時(shí) ,刪除相應(yīng)的節(jié)點(diǎn)即可鹦蠕,其余客戶端再次進(jìn)入競(jìng)爭(zhēng)創(chuàng)建節(jié)點(diǎn)冒签,直到所有的客戶端都能獲得鎖。完成自己的業(yè)務(wù)邏輯钟病。
2 利用臨時(shí)順序節(jié)點(diǎn)實(shí)現(xiàn)共享鎖的一般做法
Zookeeper中有一種節(jié)點(diǎn)叫做順序節(jié)點(diǎn)萧恕,故名思議,假如我們?cè)?lock/目錄下創(chuàng)建節(jié)3個(gè)點(diǎn)肠阱,ZooKeeper集群會(huì)按照提起創(chuàng)建的順序來(lái)創(chuàng)建節(jié)點(diǎn)票唆,節(jié)點(diǎn)分別為/test/lock/0000000001、/test/lock/0000000002屹徘、/test/lock/0000000003走趋。
ZK的臨時(shí)節(jié)點(diǎn)特性 ?當(dāng)客戶端與ZK集群斷開(kāi)連接,則臨時(shí)節(jié)點(diǎn)自動(dòng)被刪除噪伊。
利用上面這兩個(gè)特性吆视,我們來(lái)看下獲取實(shí)現(xiàn)分布式鎖的基本邏輯:
客戶端調(diào)用create()方法創(chuàng)建名為“test/lock-*”的節(jié)點(diǎn)典挑,需要注意的是,這里節(jié)點(diǎn)的創(chuàng)建類型需要設(shè)置為EPHEMERAL_SEQUENTIAL啦吧。
客戶端調(diào)用getChildren(“l(fā)ock-*”)方法來(lái)獲取所有已經(jīng)創(chuàng)建的子節(jié)點(diǎn),同時(shí)在這個(gè)節(jié)點(diǎn)上注冊(cè)上子節(jié)點(diǎn)變更通知的Watcher拙寡。
客戶端獲取到所有子節(jié)點(diǎn)path之后授滓,循環(huán)子節(jié)點(diǎn) 發(fā)現(xiàn)創(chuàng)建的節(jié)點(diǎn)是所有節(jié)點(diǎn)中序號(hào)最小的,那么就認(rèn)為這個(gè)客戶端獲得了鎖肆糕。如果發(fā)現(xiàn)循環(huán)過(guò)程中發(fā)現(xiàn)自己創(chuàng)建的節(jié)點(diǎn)不是最小的般堆,說(shuō)明自己還沒(méi)有獲取到鎖,就開(kāi)始等待诚啃,直到下次子節(jié)點(diǎn)變更通知的時(shí)候淮摔,再進(jìn)行子節(jié)點(diǎn)的獲取,判斷是否獲取鎖始赎。
我知道的就這兩種方式和橙,可能有其他的方式,網(wǎng)上有人說(shuō)造垛,還有其他的方式魔招,具體的我沒(méi)有深入,也就不知道了五辽,如果你深入了解的話办斑,歡迎來(lái)信交流。
而在實(shí)際的項(xiàng)目中杆逗,我們通常會(huì)采取第一種方式乡翅,因?yàn)榈诙N方式有一個(gè)不好的地方:
即在獲取所有的子點(diǎn),判斷自己創(chuàng)建的節(jié)點(diǎn)是否已經(jīng)是序號(hào)最小的節(jié)點(diǎn)”罪郊,這個(gè)過(guò)程蠕蚜,在整個(gè)分布式鎖的競(jìng)爭(zhēng)過(guò)程中,大量重復(fù)運(yùn)行排龄,并且絕大多數(shù)的運(yùn)行結(jié)果都是判斷出自己并非是序號(hào)最小的節(jié)點(diǎn)波势,從而繼續(xù)等待下一次通知——這個(gè)顯然看起來(lái)不怎么科學(xué)¢衔客戶端無(wú)端的接受到過(guò)多的和自己不相關(guān)的事件通知尺铣,這如果在集群規(guī)模大的時(shí)候,會(huì)對(duì)Server造成很大的性能影響争舞,并且如果一旦同一時(shí)間有多個(gè)節(jié)點(diǎn)的客戶端斷開(kāi)連接凛忿,這個(gè)時(shí)候,服務(wù)器就會(huì)像其余客戶端發(fā)送大量的事件通知 這是一個(gè)不友好的方式竞川。所以我們一般利用ZK來(lái)實(shí)現(xiàn)我們分布式鎖的時(shí)候 通常會(huì)采用第一種方式來(lái) 實(shí)現(xiàn)店溢。
像我們做游戲sdk 實(shí)時(shí)語(yǔ)音的叁熔,經(jīng)常對(duì)接游戲Cp 用戶量對(duì)于我們來(lái)說(shuō)是很大,玩家通過(guò)我們的語(yǔ)音服務(wù)在游戲交流床牧。那么場(chǎng)景來(lái)了:
? ? 不同的游戲方 對(duì)于我們來(lái)說(shuō) 是不同的應(yīng)用 用戶登入我們實(shí)時(shí)語(yǔ)音時(shí) 我們是根據(jù)應(yīng)用隨機(jī)分配服務(wù)器的荣回,那么在并發(fā)量大的時(shí)候 肯定在分配的時(shí)候 存在競(jìng)爭(zhēng),而我們的服務(wù) 又是分布式的 此時(shí)分布式鎖的場(chǎng)景就出現(xiàn)了戈咳。我們利用第一種方式來(lái)實(shí)現(xiàn)我們的分布式鎖和自己的業(yè)務(wù) 心软。
由于我們是用curator 來(lái)實(shí)現(xiàn)我們分布式鎖,而curator實(shí)現(xiàn)分布式鎖有好幾種鎖的方式:
1 共享鎖? InterProcessMutex
2 讀寫(xiě)鎖? InterProcessReadWriteLock
3 共享信號(hào)量 InterProcessSemaphoreV2
大概就這么幾種吧? 而我們的實(shí)際的項(xiàng)目中用的是InterProcessMutex來(lái)實(shí)現(xiàn)分布式鎖
此鎖屬于可重入式鎖著蛙,當(dāng)一個(gè)客戶端獲取到lock鎖之后删铃,可以重復(fù)調(diào)用acquire()而不會(huì)發(fā)生阻塞√けぃ基于InterProcessSemaphoreMutex實(shí)現(xiàn)的分布式的分布式鎖是不可重入的猎唁,當(dāng)一個(gè)客戶端獲取到lock鎖之后,再次調(diào)用acquire方法獲取鎖時(shí)會(huì)發(fā)生阻塞顷蟆〗胗纾基于InterProcessReadWriteLock實(shí)現(xiàn)的分布式鎖里邊包含了讀鎖與寫(xiě)鎖,其中讀鎖與讀鎖互斥慕的,讀鎖與寫(xiě)鎖互斥阎肝,讀鎖與讀鎖不互斥。所以我們就選擇了InterProcessMutex 來(lái)實(shí)現(xiàn)吧肮街。
在分配服務(wù)的時(shí)候 實(shí)現(xiàn)分布式鎖:
釋放鎖:
獲取鎖:
其他兩種鎖的實(shí)現(xiàn)方式如下:
InterProcessReadWriteLock簡(jiǎn)單的一個(gè)實(shí)例:
InterProcessSemaphoreV2 簡(jiǎn)單的一個(gè)實(shí)例:
關(guān)于ZK的分布式鎖大概就介紹到這里吧 我對(duì)ZK的分布式鎖也就了解的這么多了风题,如果你比我更深入的話 歡迎私信 我等渣渣向大神學(xué)習(xí)。
zk啟動(dòng)的時(shí)候 我們可以優(yōu)化我們的配置 比如以下幾種:
1 如果zk jvm內(nèi)存不夠的時(shí)候 我們要適當(dāng)?shù)脑黾樱?/p>
更改{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}”…
即可增加內(nèi)存。
2 zoo.cfg 配置文件 ?是zk的集群基本配置文件 ?調(diào)整里面的參數(shù)可以很好的保持我們的集群服務(wù)穩(wěn)定绕辖。所以大家要對(duì)里面的配置熟悉摇肌。如下:
1.tickTime:CS通信心跳數(shù)
Zookeeper 服務(wù)器之間或客戶端與服務(wù)器之間維持心跳的時(shí)間間隔,也就是每個(gè) tickTime 時(shí)間就會(huì)發(fā)送一個(gè)心跳仪际。tickTime以毫秒為單位
tickTime=2000
2.initLimit:LF初始通信時(shí)限
集群中的follower服務(wù)器(F)與leader服務(wù)器(L)之間初始連接時(shí)能容忍的最多心跳數(shù)(tickTime的數(shù)量)
initLimit=10
3.syncLimit:LF同步通信時(shí)限
集群中的follower服務(wù)器與leader服務(wù)器之間請(qǐng)求和應(yīng)答之間能容忍的最多心跳數(shù)(tickTime的數(shù)量)围小。
syncLimit=5
4.dataDir:數(shù)據(jù)文件目錄
Zookeeper保存數(shù)據(jù)的目錄,默認(rèn)情況下树碱,Zookeeper將寫(xiě)數(shù)據(jù)的日志文件也保存在這個(gè)目錄里肯适。
dataDir=E:\\comany\\zookeeper\\zkdata\\data1
5.dataLogDir:日志文件目錄
Zookeeper保存日志文件的目錄。
dataLogDir=E:\\comany\\zookeeper\\zkdata\\logs1
6.clientPort:客戶端連接端口
客戶端連接 Zookeeper 服務(wù)器的端口成榜,Zookeeper 會(huì)監(jiān)聽(tīng)這個(gè)端口框舔,接受客戶端的訪問(wèn)請(qǐng)求。
clientPort=2181
7.服務(wù)器名稱與地址:集群信息(服務(wù)器編號(hào),服務(wù)器地址刘绣,LF通信端口樱溉,選舉端口)
這個(gè)配置項(xiàng)的書(shū)寫(xiě)格式比較特殊,規(guī)則如下:
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的分布式鎖和服務(wù)優(yōu)化也就這些了福贞,只怪自己太渣,沒(méi)辦法移斩,只能講到這個(gè)程度肚医,時(shí)間如白駒過(guò)隙,一晃之間就到了9月向瓷,一年也就快過(guò)完了,自己也越來(lái)越年長(zhǎng)舰涌,自己還是一如既往的渣渣猖任,危機(jī)感也越來(lái)越重,愿未來(lái)更要自我約束瓷耙,自我鞭策朱躺,自我學(xué)習(xí)。
時(shí)間不早了 該回去了? 晚上在產(chǎn)業(yè)園吃了碗面 完全不管飽搁痛,身體要緊长搀,回去補(bǔ)點(diǎn)狗糧,我是小志碼字鸡典,一個(gè)簡(jiǎn)單碼代碼的小人物源请。如果想了解這個(gè)項(xiàng)目和代碼? 加我微信? 微信號(hào):2B青年? 歡迎交流 相互學(xué)習(xí)。
補(bǔ)一張公司加班狗的圖照: