Zookeeper是分布式系統(tǒng)中的中間件港谊,負(fù)責(zé)分布式系統(tǒng)的協(xié)調(diào)服務(wù)达舒。可用于
- master選舉
- 實(shí)現(xiàn)配置文件的統(tǒng)一管理
- 實(shí)現(xiàn)分布式鎖
Zookeeper文件系統(tǒng)
zk是1個(gè)類似Linux目錄的文件系統(tǒng),它是1個(gè)樹形結(jié)構(gòu)额获。zk的節(jié)點(diǎn)稱為znode霜浴,znode可以存放數(shù)據(jù)晶衷,也可以掛載子節(jié)點(diǎn)。znode分為4類
- 永久節(jié)點(diǎn)阴孟,客戶端與zk斷開連接后晌纫,節(jié)點(diǎn)依然存在
- 永久順序節(jié)點(diǎn),和永久節(jié)點(diǎn)一樣永丝,客戶端與zk斷開連接后锹漱,節(jié)點(diǎn)依然存在,不過zk會(huì)為永久順序節(jié)點(diǎn)的name加上順序
- 臨時(shí)節(jié)點(diǎn)慕嚷,客戶端與zk斷開連接后哥牍,節(jié)點(diǎn)會(huì)被自動(dòng)刪除
- 臨時(shí)順序節(jié)點(diǎn),和臨時(shí)節(jié)點(diǎn)一樣闯冷,斷開連接后砂心,臨時(shí)節(jié)點(diǎn)被刪除,不過zk會(huì)為它的name加上順序
臨時(shí)節(jié)點(diǎn)蛇耀,在客戶端與zk斷開連接辩诞,并且心跳超時(shí)后,會(huì)被zk自動(dòng)刪除纺涤。這種機(jī)制可以用來實(shí)現(xiàn)分布式鎖译暂,應(yīng)用場景:如果分布式系統(tǒng)中的1個(gè)進(jìn)程持有鎖抠忘,在它執(zhí)行過程中發(fā)生異常而崩潰,這樣它不會(huì)釋放鎖外永,導(dǎo)致所有獲取鎖的進(jìn)程永久等待而死鎖崎脉。使用臨時(shí)節(jié)點(diǎn),如果客戶端崩潰伯顶,臨時(shí)節(jié)點(diǎn)會(huì)被zk自動(dòng)刪除囚灼,避免發(fā)生死鎖
節(jié)點(diǎn)屬性包括:
- czxid,創(chuàng)建znode事務(wù)的id
- mzxid祭衩,修改znode事務(wù)的id
- znode創(chuàng)建時(shí)間和修改時(shí)間
- 節(jié)點(diǎn)類型
- cversion灶体,節(jié)點(diǎn)版本號,每個(gè)節(jié)點(diǎn)都有自己的版本號掐暮,當(dāng)節(jié)點(diǎn)變化時(shí)蝎抽,版本號會(huì)+1
- dataversion,數(shù)據(jù)版本號
- aclversion
- numchildren路克,子節(jié)點(diǎn)個(gè)數(shù)
事務(wù)
zk將改變zkServer的操作樟结,稱為事務(wù)。事務(wù)包括精算,對節(jié)點(diǎn)的增刪改操作瓢宦,以及客戶端連接Session的創(chuàng)建和銷毀
對于每1個(gè)事務(wù),zk都生成1個(gè)全局唯一的數(shù)值型事務(wù)id灰羽,ZXID刁笙,通過ZXID可以知道zk處理請求的先后順序
ACL
ACL是訪問控制列表,ACL用來對znode進(jìn)行權(quán)限控制
ACL包括3部分:權(quán)限模式schema谦趣,授權(quán)對象和權(quán)限permission
schema包括ip,digest座每,world和super
ip前鹅,按照ip進(jìn)行權(quán)限控制,配置以外ip的客戶端不能訪問節(jié)點(diǎn)
digest峭梳,最常用舰绘,類似于用戶名密碼,zk會(huì)使用加密算法對其進(jìn)行加密
world葱椭,所有用戶都能訪問捂寿,類似于不設(shè)置限制
super,超級管理員授權(quán)對象孵运,和權(quán)限模式schema結(jié)合使用
(1)如果權(quán)限模式選擇ip秦陋,授權(quán)對象就是ip地址
(2)如果digest,授權(quán)對象就是用戶名密碼
(3)如果world治笨,授權(quán)對象只有1個(gè)驳概,即anyone
(4)如果super赤嚼,授權(quán)對象就是超級管理員的用戶名密碼權(quán)限,即針對節(jié)點(diǎn)能進(jìn)行何種操作
C D R W A
創(chuàng)建 刪除 讀操作 寫操作 管理(即對節(jié)點(diǎn)設(shè)置ACL的權(quán)限)
會(huì)話Session
客戶端與zk通過會(huì)話Session交互顺又,通過Session
- 發(fā)送心跳
- 發(fā)送請求更卒,接收相應(yīng)
- 接收Watch事件通知
值得注意的是Session的超時(shí)時(shí)間
- 使用Java構(gòu)造Zk對象時(shí),需要客戶端配置1個(gè)Session超時(shí)時(shí)間
- 最終Session的超時(shí)時(shí)間由服務(wù)器端與客戶端進(jìn)行協(xié)商決定稚照,服務(wù)器端默認(rèn)Session最小超時(shí)時(shí)間為2個(gè)tickTime蹂空,即4s,默認(rèn)Session最大超時(shí)時(shí)間為20個(gè)tickTime果录,即40s
- 當(dāng)客戶端指定的超時(shí)時(shí)間小于服務(wù)器端指定minTimeOut時(shí)上枕,使用服務(wù)器端的minTimeOut作為超時(shí)時(shí)間,當(dāng)大于時(shí)雕憔,使用服務(wù)器端的maxTimeOut作為超時(shí)時(shí)間
Watch
Znode發(fā)生變化姿骏,例如Znode的增刪改和子節(jié)點(diǎn)發(fā)生變化時(shí),可以通過Watch機(jī)制通知客戶端斤彼。Watch包括一些事件分瘦,包括:
- 客戶端與服務(wù)器端斷開連接時(shí),None
- 創(chuàng)建節(jié)點(diǎn) 琉苇,NodeCreated
- 刪除節(jié)點(diǎn)嘲玫,NodeDeleted
- 節(jié)點(diǎn)數(shù)據(jù)變化,NodeDataChanged并扇,本質(zhì)上只關(guān)注節(jié)點(diǎn)的數(shù)據(jù)版本號dataVersion去团,當(dāng)dataVersion變化時(shí),會(huì)發(fā)送NodeDataChanged事件
- 節(jié)點(diǎn)子節(jié)點(diǎn)變化穷蛹,NodeChildrenChanged土陪,只關(guān)注子節(jié)點(diǎn)個(gè)數(shù)的變更,子節(jié)點(diǎn)數(shù)據(jù)變化不會(huì)觸發(fā)
Watch是輕量級的肴熏,是對本地JVM中方法的回調(diào)鬼雀,服務(wù)器端只存儲(chǔ)znode是否設(shè)置了Watch,當(dāng)znode發(fā)生對于Watch事件時(shí)蛙吏,服務(wù)器會(huì)向客戶端發(fā)送消息源哩,客戶端收到消息后觸發(fā)Watch的回調(diào)方法
Leader選舉
zk一共有3種角色,Leader鸦做、Follower和Observer
- Leader負(fù)責(zé)處理zk事務(wù)励烦,也就是對znode的增刪改操作和客戶端連接的創(chuàng)建和銷毀,發(fā)起投票
- Follower接收客戶端的非事務(wù)操作請求并處理泼诱,向leader轉(zhuǎn)發(fā)事務(wù)操作坛掠,參與投票
- Observer是觀察者,和Follower功能類似,接收并處理非事務(wù)操作却音,向leader轉(zhuǎn)發(fā)事務(wù)操作改抡。和Follower不同的是,Observer不參與投票系瓢,只是為了提升系統(tǒng)讀取速度
zk服務(wù)器一共有3種狀態(tài)阿纤,Looking、Leading和Following
- Looking夷陋,是集群正在進(jìn)行l(wèi)eader選舉欠拾,處于無主狀態(tài)
- Leading,集群的leader處于Leading狀態(tài)
- Following骗绕,集群的非leader處于Following狀態(tài)
Leader選舉藐窄,分為2種情況:服務(wù)器啟動(dòng)時(shí)的選舉,集群運(yùn)行期間leader崩潰觸發(fā)的選舉
選票由2部分組成酬土,SID和ZXID荆忍,SID即zk服務(wù)器id,也就是配置文件中的myid撤缴;ZXID為事務(wù)ID
-
服務(wù)器啟動(dòng)時(shí)的選舉流程
第1輪投票刹枉,每個(gè)Server都將選票投給自己,即SID為自己的myid屈呕,ZXID為事務(wù)ID微宝;
之后接收其它Server的選票并進(jìn)行驗(yàn)證投票有效性,驗(yàn)證包括:檢查投票是否是本輪的投票虎眨,還有投票的Server是否處于Looking狀態(tài)蟋软;
驗(yàn)證后進(jìn)行PK,對于其它Server的投票嗽桩,都與自己的投票進(jìn)行PK岳守,PK規(guī)則是:- 先比較選票的ZXID,ZXID大的獲勝
- 如果ZXID相等碌冶,比較SID棺耍,SID大的獲勝
經(jīng)過多次PK之后,每個(gè)Server都會(huì)選出1個(gè)leader种樱;
對結(jié)果進(jìn)行統(tǒng)計(jì),如果有超過半數(shù)(>n/2+1)Server的投票相同俊卤,則本輪投票結(jié)果成為leader嫩挤;否則繼續(xù)下一輪投票,直到選出leader
一旦Leader確定消恍,Leader進(jìn)入Leading狀態(tài)岂昭,F(xiàn)ollower進(jìn)入Following狀態(tài)
運(yùn)行期間leader崩潰,觸發(fā)leader選舉
當(dāng)leader崩潰時(shí)狠怨,集群中所有節(jié)點(diǎn)都會(huì)將節(jié)點(diǎn)狀態(tài)更改為Looking狀態(tài)约啊,然后進(jìn)入投票
和啟動(dòng)時(shí)選舉一樣邑遏,第1輪投票,每個(gè)Server都會(huì)將自己作為leader進(jìn)行投票恰矩,經(jīng)過驗(yàn)證记盒,PK和統(tǒng)計(jì)后選出新的leader
Apache Curator
原生的ZK API有許多缺點(diǎn):
- 超時(shí)不重連,需要手動(dòng)重連
- Watch只能使用1次外傅,不能多次使用
- 無法遞歸操作節(jié)點(diǎn)
Apache Curator解決了原生ZK的這些缺點(diǎn)纪吮,并且提供了一些分布式環(huán)境下的zk輔助類,例如分布式鎖InterProcessMutex萎胰,類似于CountDownLatch的分布式計(jì)數(shù)器碾盟,類似于CyclicBarrier的回環(huán)柵欄等
針對原生API無法自動(dòng)超時(shí)重連的情況,Curator提供了RetryPolicy重試策略接口技竟,提供了幾種重試策略冰肴。包括:
(1)重試1次,可設(shè)置間隔時(shí)間
(2)重試N次榔组,可設(shè)置間隔時(shí)間
(3)永遠(yuǎn)重試熙尉,直到連接成功
(4)設(shè)置間隔時(shí)間,一直重試瓷患,直到累計(jì)等待時(shí)間超過閾值
(5)設(shè)置重試次數(shù)骡尽,重試時(shí)間隨機(jī)生成的策略Curator的Watch是可以重復(fù)使用的
提供了3個(gè)類對節(jié)點(diǎn)進(jìn)行監(jiān)聽,其中
NodeCache用于監(jiān)聽節(jié)點(diǎn)自身變化
PathChildrenCache用于監(jiān)聽節(jié)點(diǎn)的子節(jié)點(diǎn)變化
TreeCache不但可以用來監(jiān)聽節(jié)點(diǎn)自身變化擅编,還同時(shí)可以監(jiān)聽子節(jié)點(diǎn)的變化Curator可以遞歸的操作節(jié)點(diǎn)攀细,原生API中,創(chuàng)建znode需要先創(chuàng)建其父節(jié)點(diǎn)爱态,刪除znode需要先刪除其子節(jié)點(diǎn)谭贪,否則操作失敗锦担;Curator可以遞歸的創(chuàng)建和刪除俭识,使用creatingParentsIfNeeded()和deletingChildrenIfNeeded()
- Apache Curator提供了分布式系統(tǒng)中的線程同步輔助類,提供了分布式鎖InterProcessMutex洞渔,分布式信號量InterProcessSemaphoreV2套媚,分布式計(jì)數(shù)器,分布式回環(huán)柵欄等磁椒。
以分布式鎖InterProcessMutex為例
(1)分布式系統(tǒng)中需要同步的所有進(jìn)程堤瘤,在zk鎖節(jié)點(diǎn)下,創(chuàng)建子節(jié)點(diǎn)浆熔,這個(gè)子節(jié)點(diǎn)是臨時(shí)有序節(jié)點(diǎn)本辐。1st創(chuàng)建的是lock0, 2nd是lock1, 以此類推
(2)當(dāng)客戶端創(chuàng)建的臨時(shí)節(jié)點(diǎn),是zk鎖節(jié)點(diǎn)的所有子節(jié)點(diǎn)中最小的時(shí),它就獲取到了鎖慎皱,可以對共享資源進(jìn)行訪問老虫。否則,向比它小1的節(jié)點(diǎn)設(shè)置Watch茫多,并阻塞等待
(3)當(dāng)客戶端完成對共享資源的操作祈匙,會(huì)釋放鎖,釋放鎖就是將自己創(chuàng)建的臨時(shí)節(jié)點(diǎn)刪除地梨。當(dāng)臨時(shí)節(jié)點(diǎn)被刪除時(shí)菊卷,會(huì)觸發(fā)比它序號大1的節(jié)點(diǎn)在其上面設(shè)置的Watch,對下一個(gè)節(jié)點(diǎn)進(jìn)行通知宝剖,被通知的節(jié)點(diǎn)成為zk鎖節(jié)點(diǎn)的所有子節(jié)點(diǎn)中最小的洁闰,獲得鎖,對共享資源進(jìn)行訪問
這里面有幾個(gè)比較巧妙的做法
客戶端創(chuàng)建的是臨時(shí)節(jié)點(diǎn)
創(chuàng)建臨時(shí)節(jié)點(diǎn)万细,是為了防止客戶端獲取到鎖扑眉,正在訪問共享資源時(shí),客戶端崩潰赖钞,而未刪除它創(chuàng)建的節(jié)點(diǎn)腰素,導(dǎo)致所有的客戶端持續(xù)等待,而死鎖雪营。
使用臨時(shí)節(jié)點(diǎn)弓千,當(dāng)客戶端崩潰時(shí),客戶端與zk的session連接也會(huì)斷開献起,而臨時(shí)節(jié)點(diǎn)會(huì)在session斷開時(shí)洋访,被zk自動(dòng)刪除,防止死鎖的發(fā)生在獲取鎖的過程中谴餐,客戶端會(huì)判斷自己創(chuàng)建的節(jié)點(diǎn)是否是當(dāng)前序號最小的姻政,若是,則獲取到鎖岂嗓,執(zhí)行程序汁展;否則向比它序號小1的節(jié)點(diǎn)設(shè)置Watch。如果客戶端發(fā)現(xiàn)自己不是最小的厌殉,但還未來得及向比它序號小1的節(jié)點(diǎn)設(shè)置Watch食绿,比它序號小1的節(jié)點(diǎn)就被刪除,節(jié)點(diǎn)不存在了公罕,無法設(shè)置Watch器紧,而客戶端創(chuàng)建的節(jié)點(diǎn)成為序號最小的,但卻無法被通知熏兄,導(dǎo)致所有客戶端持續(xù)等待而死鎖。
針對這種情況,Apache Curator保證判斷節(jié)點(diǎn)是否序號最小這個(gè)操作摩桶,和向比其序號小1節(jié)點(diǎn)設(shè)置Watch這個(gè)操作是原子性的桥状,在這個(gè)兩個(gè)操作中間,不會(huì)有其它的Client進(jìn)行操作硝清,比它序號小1的節(jié)點(diǎn)也就不會(huì)被刪除