ZooKeeper實現(xiàn)分布式的思路
Hadoop生態(tài)系統(tǒng)為開源屆提供很多優(yōu)秀軟件,zookeeper便是其中一員。
前段時間項目中用到了zookeeper,主要是用作服務(wù)的注冊和發(fā)現(xiàn)使用方式類似阿里的dubbo。實際上zookeeper的功能不僅僅只有這些內(nèi)容,它提供了一系列非常方便使用的功能,后面會提到。這篇文章僅僅是我個人的一點兒理解,如有錯誤煩請指正,以免給別人誤導(dǎo)。
1、zookeeper是什么
zookeeper的名字很有趣被稱為動物管理員,這是因為Hadoop生態(tài)系統(tǒng)中很多軟件的名字都是動物,hadoop本身就是小象的意思,還有hive小蜜蜂,pig。zookeeper作為一個分布式協(xié)調(diào)系統(tǒng)在hadhoop中被廣泛的應(yīng)用,其中HBase默認帶有zookeeper。zookeeper主要功能有配置維護、分布式鎖、選舉、分布式隊列等,并且zookeeper本身可以是一個集群,提供了高可用性。這一切的功能都離不開zookeeper的數(shù)據(jù)模型。
2、zookeeper數(shù)據(jù)模型
zookeeper提供的命名服務(wù)看起來和一個unix的文件系統(tǒng)非常相似,下面是從官網(wǎng)復(fù)制的一張圖:
其中的每個節(jié)點稱為znode,每個znode節(jié)點既可以包含數(shù)據(jù)又可以包含子節(jié)點,由于zookeeper被定位為協(xié)調(diào)程序因此znode中的數(shù)據(jù)通常存儲的是非常小的數(shù)據(jù),比如狀態(tài)信息,位置信息等等。znode中有一個很重要的概念——節(jié)點類型,znode有兩種類型的節(jié)點:臨時節(jié)點,永久節(jié)點。其中這兩種節(jié)點又分為有序和無序,重點講一下臨時節(jié)點,因為zk中很多基礎(chǔ)的功能都是基于臨時節(jié)點實現(xiàn)的,client在和zookeeper連接的時候兩者之間會建立起session,session的狀態(tài)由zookeeper服務(wù)端維護,臨時節(jié)點的特點是隨著session的超時服務(wù)端會將client建立的所有臨時節(jié)點移除,而永久節(jié)點即使客戶端退出節(jié)點也不會消失,同時臨時節(jié)點不能有子節(jié)點但是可以掛載數(shù)據(jù)。結(jié)合watcher機制可以實現(xiàn)非常豐富和靈活的功能。
3、zookeeper集群結(jié)構(gòu)
zookeeper本身支持單機部署和集群部署,生產(chǎn)環(huán)境建議使用集群部署,因為集群部署不存在單點故障問題,并且zookeeper建議部署的節(jié)點個數(shù)為奇數(shù)個,只有超過一半的機器不可用整個zk集群才不可用。zookeeper集群中主要有兩個角色leader和flower,每個客戶端可以連接集群中的任何一個zookeeper節(jié)點,同時從其上面read信息,但是針對write操作,flower節(jié)點會轉(zhuǎn)發(fā)給leader,由leader負責原子廣播,從而保證集群中各個節(jié)點的數(shù)據(jù)一致性,zookeeper中規(guī)定只有當多余一半的節(jié)點同步完成整個write操作才算完成。也就是說可能會有少于一半的數(shù)據(jù)不是新數(shù)據(jù),因此zookeeper中不是強一致性而是實現(xiàn)的最終一致性。但是客戶端可以使用sync來強制讀取最新的數(shù)據(jù)。
4、replaction
zookeeper中的高可用性是通過數(shù)據(jù)冗余和實現(xiàn)的,也就是一份數(shù)據(jù)存在多個節(jié)點中,zookeeper中要求同一份數(shù)據(jù)需要在超過一半的節(jié)點上存在,只有這樣才能實現(xiàn)對宕機數(shù)量的容忍度更高。zk建議配置奇數(shù)個節(jié)點,是因為在flower同步數(shù)據(jù)和進行l(wèi)eader選舉的時候都要求有超過一半完成或同意才算ok。舉例來說,假如有3個節(jié)點,至少需要有2個節(jié)點正常,就是容忍度為1(允許宕掉的節(jié)點數(shù)),有4個節(jié)點,至少需要有三個節(jié)點正常,容忍度同樣為1,多出來一個機器但是容忍度相同在任何時候看來都得不償失。因此zk建議部署奇數(shù)個節(jié)點,但這不是強制。另外再看一下為什么寫操作的時候要求至少有超過一半節(jié)點commit成功整體才成功,假如有2t+1個zk節(jié)點,也就是必須有t+1個節(jié)點commit成功才算成功,因為只有這種情況下才能達成至少有一個節(jié)點存有前后兩次的更新操作(兩次t+1節(jié)點至少會重復(fù)一個)。zookeeper使用zab算法實現(xiàn)數(shù)據(jù)的原子廣播,并且每次write會寫日志然后更新緩存,每個zk節(jié)點維護一個zxid,zxid是一個全局變量,隨著znode的每一次改變而遞增,當leader掛掉的時候,剩余的flower選擇zxid最大的節(jié)點作為新的leader,在新leader提供服務(wù)前還需要一次數(shù)據(jù)恢復(fù),新leader只是擁有最多的數(shù)據(jù),但不一定擁有最新的數(shù)據(jù),因此leader和flower的數(shù)據(jù)需要同步到最新的狀態(tài),通過合并的過程完成整個數(shù)據(jù)的恢復(fù)。
上圖5個zk節(jié)點允許兩個宕機,其他三個節(jié)點總是能恢復(fù)出來ABCDE。
#p#
5、Watch機制
zookeeper允許客戶端對znode節(jié)點或者節(jié)點中的數(shù)據(jù)設(shè)置監(jiān)聽器,當znode改變的時候服務(wù)器觸發(fā)監(jiān)聽,客戶端完成一個回調(diào)做自己需要處理的邏輯。zookeeper中的watch是一次性的,也就是當監(jiān)聽觸發(fā)后,需要再次應(yīng)用watcher,下次才能在收到變化的通知。exists,getData,getChildren接口都可以指定是否應(yīng)用watcher,可以使用默認的watcher或者自定義watcher。觸發(fā)watcher的可以為create、delete、setData、setACL。
6、配置管理
如果是單機或者幾臺機器,當應(yīng)用的配置項變更的時候,可能通過手動的方式去修改一下,但是假如一個集群中有成百上千個應(yīng)用節(jié)點,如何才能保證快速無差錯的完成配置項的變更。zookeeper的出現(xiàn)可以輕松地解決這個問題
每個節(jié)點在zk上建立永久型znode并寫入配置項,然后監(jiān)聽該節(jié)點下數(shù)據(jù)的變化,一旦其他客戶端修改了其中的數(shù)據(jù),所有的監(jiān)聽客戶端都會收到變更通知。
7、Leader選舉
zookeeper本身提供leader選舉機制,大概的思路是所有的節(jié)點創(chuàng)建臨時有序的znode然后監(jiān)聽所有節(jié)點的變化情況,獲取最小序號和自己創(chuàng)建的序列作比較,如果自己為最小則當選為leader,當主動刪除自己創(chuàng)建的節(jié)點或者leader宕機后,臨時節(jié)點消失,該變化會被其他存活的節(jié)點獲取到從而觸發(fā)第二次的leader選舉,依次類推。實際上zookeeper提到的很多recipes curator都提供了很好的實現(xiàn)(除了兩階段提交),同時基于底層的zookeeper api開發(fā)應(yīng)用需要考慮的東西很多,curator對這些都提供了封裝,所以如果要編寫zookeeper應(yīng)用推薦使用curator。
leader應(yīng)用的場景很廣泛,curator提供了兩種不同的選舉實現(xiàn),一種是輪詢做leader,另外一種是永久獲取leader權(quán)直到退出,兩種選舉實現(xiàn)可以應(yīng)用在不同的集群應(yīng)用中。HBase中使用的是獲取leader的永久權(quán)