- Hadoop大數(shù)據(jù)技術(shù)開發(fā)實戰(zhàn)
- 張偉洋
- 3717字
- 2020-03-06 11:48:44
6.1 ZooKeeper簡介
ZooKeeper是一個分布式應(yīng)用程序協(xié)調(diào)服務(wù),主要用于解決分布式集群中應(yīng)用系統(tǒng)的一致性問題。它能提供類似文件系統(tǒng)的目錄節(jié)點樹方式的數(shù)據(jù)存儲,主要用途是維護(hù)和監(jiān)控所存數(shù)據(jù)的狀態(tài)變化,以實現(xiàn)對集群的管理。
6.1.1 應(yīng)用場景
在分布式環(huán)境里,往往會有很多服務(wù)器都需要同樣的配置來保證信息的一致性和集群的可靠性,而一個分布式集群往往動輒上百臺服務(wù)器,一旦配置信息改變,就需要對每臺服務(wù)器進(jìn)行修改,這樣會消耗大量時間,那么有沒有一種簡單的方法統(tǒng)一對其修改呢?像這樣的配置信息完全可以交給 ZooKeeper來管理,將配置信息保存在 ZooKeeper的某個目錄節(jié)點中,然后所有應(yīng)用服務(wù)器都監(jiān)控配置信息的狀態(tài),一旦配置信息發(fā)生變化,每臺應(yīng)用服務(wù)器就會收到 ZooKeeper的通知,然后從 ZooKeeper獲取新的配置信息應(yīng)用到系統(tǒng)中即可。
1. 統(tǒng)一命名服務(wù)
利用ZooKeeper中的樹形分層結(jié)構(gòu),可以把系統(tǒng)中的各種服務(wù)的名稱、地址以及目錄信息存放在ZooKeeper中,需要的時候去ZooKeeper中讀取就可以了。
此外,ZooKeeper中有一種節(jié)點類型是順序節(jié)點,可以利用它的這個特性制作序列號。我們都知道,數(shù)據(jù)庫有主鍵ID可以自動生成,但是在分布式環(huán)境中就無法使用了,于是我們可以使用ZooKeeper的命名服務(wù),它可以生成有順序的編號,而且支持分布式,非常方便。
2. 集群管理
ZooKeeper能夠很容易地實現(xiàn)集群管理的功能,如有多臺服務(wù)器組成一個服務(wù)集群,那么必須要有一個“總管”知道當(dāng)前集群中每臺機(jī)器的服務(wù)狀態(tài),一旦有服務(wù)器不能提供服務(wù),集群中其他服務(wù)器必須知道,從而做出調(diào)整,重新分配服務(wù)策略。當(dāng)增加一臺或多臺服務(wù)器時,同樣也必須讓“總管”知道。
ZooKeeper不僅能夠幫助我們維護(hù)當(dāng)前集群中服務(wù)器的服務(wù)狀態(tài),而且能夠選舉出一個“總管”,讓這個“總管”來管理集群,這種選舉方式稱為“Leader選舉”。
3. 分布式鎖
在一個分布式環(huán)境中,為了提高可靠性,集群的每臺服務(wù)器上都部署著同樣的服務(wù)。但是一個常見的問題就是,如果集群中的每臺服務(wù)器都進(jìn)行同一件事情的話,它們相互之間就要協(xié)調(diào),編程起來將非常復(fù)雜。這個時候可以使用分布式鎖,我們可以利用ZooKeeper來協(xié)調(diào)多個分布式進(jìn)程之間的活動,在某個時刻只讓一個服務(wù)去工作,當(dāng)這個服務(wù)出現(xiàn)問題的時候?qū)㈡i釋放,立即切換到另外的服務(wù)。
6.1.2 架構(gòu)原理
ZooKeeper集群的總體架構(gòu)如圖6-1所示。
ZooKeeper集群由一組服務(wù)器(Server)節(jié)點組成,在這些服務(wù)器節(jié)點中有一個節(jié)點的角色為Leader,其他節(jié)點的角色為Follower。當(dāng)客戶端(Client)連接到ZooKeeper集群并執(zhí)行寫請求時,這些請求首先會被發(fā)送到Leader節(jié)點。Leader節(jié)點在接收到數(shù)據(jù)變更請求后,首先會將該變更寫入到本地磁盤以作恢復(fù)使用,當(dāng)所有的寫請求持久化到磁盤后,會將數(shù)據(jù)變更應(yīng)用到內(nèi)存中,以加快數(shù)據(jù)讀取速度,最后Leader節(jié)點上的數(shù)據(jù)變更會同步(廣播)到集群的其他Follower節(jié)點上。

圖6-1 ZooKeeper集群的總體架構(gòu)
當(dāng)Leader節(jié)點發(fā)生故障而失效時,F(xiàn)ollower節(jié)點會快速響應(yīng),由消息層重新選出一個Leader節(jié)點來處理客戶端請求。
6.1.3 數(shù)據(jù)模型
ZooKeeper主要用于管理協(xié)調(diào)數(shù)據(jù)(服務(wù)器的配置、狀態(tài)等信息),不能用于存儲大型數(shù)據(jù)集。
ZooKeeper有一個樹形層次的命名空間,該命名空間的組織方式類似于標(biāo)準(zhǔn)文件系統(tǒng)。ZooKeeper可以將該命名空間共享給分布式應(yīng)用程序,使它們可以利用該命名空間進(jìn)行相互協(xié)調(diào)。與為存儲而設(shè)計的典型文件系統(tǒng)不同,ZooKeeper數(shù)據(jù)保存在內(nèi)存中,這樣可以提高吞吐量和降低數(shù)據(jù)延遲。
在ZooKeeper的命名空間中,名稱是由斜線(/)分隔的路徑元素組成的。命名空間中的每個名稱(也叫節(jié)點)都由路徑標(biāo)識,如圖6-2所示。

圖6-2 ZooKeeper數(shù)據(jù)模型
ZooKeeper命名空間中的每個節(jié)點都可以有與之關(guān)聯(lián)的數(shù)據(jù)(也稱元數(shù)據(jù))以及子節(jié)點,就好比標(biāo)準(zhǔn)文件系統(tǒng)中的每個文件夾都可以存放文件并且每個文件夾都有子文件夾。
通常使用znode來表示ZooKeeper命名空間中的名稱節(jié)點,存儲在每個znode上的數(shù)據(jù)會被客戶端原子化地讀取和寫入。讀取操作可以獲取與znode關(guān)聯(lián)的所有數(shù)據(jù),而寫入操作可以替換所有數(shù)據(jù)。znode的主要特點如下:
- znode中僅存儲協(xié)調(diào)數(shù)據(jù),即與同步相關(guān)的數(shù)據(jù),例如狀態(tài)信息、配置內(nèi)容、位置信息等,因此數(shù)據(jù)量很小,大概B到KB量級。
- 一個znode維護(hù)一個狀態(tài)結(jié)構(gòu),該結(jié)構(gòu)包括版本號、ACL(訪問控制列表)變更、時間戳。znode存儲的數(shù)據(jù)每次發(fā)生變化,版本號都會遞增,每當(dāng)客戶端檢索數(shù)據(jù)時,客戶端也會同時接收到數(shù)據(jù)的版本。客戶端也可以基于版本號檢索相關(guān)數(shù)據(jù)。
- 每個znode都有一個ACL,用來限定該znode的客戶端訪問權(quán)限。
- 客戶端可以在znode上設(shè)置一個觀察者(Watcher),如果該znode上的數(shù)據(jù)發(fā)生變更,ZooKeeper就會通知客戶端,從而觸發(fā)Watcher中實現(xiàn)的邏輯的執(zhí)行。
6.1.4 節(jié)點類型
ZooKeeper中的znode節(jié)點主要有以下4種類型。
1. 持久節(jié)點(PERSISTENT)
持久節(jié)點在創(chuàng)建后就一直存在,除非手動將其刪除。
2. 持久順序節(jié)點( PERSISTENT _SEQUENTIAL)
持久順序節(jié)點除了有持久節(jié)點的功能外,在創(chuàng)建時,ZooKeeper會在節(jié)點名稱末尾自動追加一個自增長的數(shù)字后綴作為新的節(jié)點名稱,以便記錄每一個節(jié)點創(chuàng)建的先后順序。數(shù)字后綴的長度是10位,且由0填充,例如0000000001。舉個例子,當(dāng)前有一個父節(jié)點/lock,我們需要在該節(jié)點下創(chuàng)建順序子節(jié)點/lock/node-,ZooKeeper在生成該子節(jié)點時會根據(jù)當(dāng)前子節(jié)點數(shù)量自動增加數(shù)字后綴,如果是第一個創(chuàng)建的子節(jié)點,則節(jié)點名稱為/lock/node-0000000000,下一個子節(jié)點則為/lock/node-0000000001,依次類推。
3. 臨時節(jié)點(EPHEMERAL)
只要創(chuàng)建節(jié)點的客戶端與ZooKeeper服務(wù)器的連接會話是活動的,這些節(jié)點就存在。當(dāng)客戶端與服務(wù)器的連接會話斷開時,節(jié)點將被刪除。基于此,臨時節(jié)點是不允許有子節(jié)點的。
4. 臨時順序節(jié)點( EPHEMERAL _SEQUENTIAL )
臨時順序節(jié)點除了有臨時節(jié)點的功能外,節(jié)點在創(chuàng)建時,會在節(jié)點末尾追加自增長的數(shù)字編號,這一點與持久順序節(jié)點的順序功能一致。
6.1.5 Watcher機(jī)制
ZooKeeper是一個基于Watcher(觀察者)模式設(shè)計的分布式服務(wù)管理框架,其允許客戶端向服務(wù)器的znode上注冊一個Watcher,一旦znode的狀態(tài)發(fā)生變化,ZooKeeper就會通知已經(jīng)在它上面注冊的Watcher做出相應(yīng)的反應(yīng)。當(dāng)前,ZooKeeper有四種狀態(tài)變化事件:節(jié)點創(chuàng)建、節(jié)點刪除、節(jié)點數(shù)據(jù)修改和子節(jié)點變更。
ZooKeeper中所有的讀取操作——getData()方法、getChildren()方法和exists()方法,都可以向服務(wù)器設(shè)置一個Watcher。Watcher事件相當(dāng)于一次性的觸發(fā)器,當(dāng)znode的數(shù)據(jù)發(fā)生改變時,會通知設(shè)置Watcher的客戶端。例如,如果客戶端執(zhí)行g(shù)etData(“/znode1”,true)方法,然后改變或刪除/znode1的數(shù)據(jù),客戶端將獲得/znode1的狀態(tài)改變事件通知。如果/znode1再次更改,則不會發(fā)送任何通知給客戶端,除非客戶端提前再次向/znode1設(shè)置Watcher。
ZooKeeper的Watcher有兩種類型:數(shù)據(jù)Watcher和子節(jié)點Watcher。數(shù)據(jù)Watcher只監(jiān)聽節(jié)點元數(shù)據(jù)的改變,子節(jié)點Watcher只監(jiān)聽節(jié)點的子節(jié)點的創(chuàng)建與刪除。getData()方法和exists()方法可以設(shè)置數(shù)據(jù)Watcher,這兩個方法返回znode節(jié)點的元數(shù)據(jù)信息。getChildren()方法可以設(shè)置子節(jié)點Watcher,該方法則返回一個子節(jié)點列表。因此,setData()方法會觸發(fā)數(shù)據(jù)Watcher,一個成功的create()方法將觸發(fā)正在創(chuàng)建的znode的數(shù)據(jù)Watcher以及父znode的子節(jié)點Watcher,一個成功的delete()方法將會為被刪除的znode觸發(fā)一個數(shù)據(jù)Watcher以及為被刪除節(jié)點的父節(jié)點觸發(fā)一個子節(jié)點Watcher。
1. Watcher機(jī)制執(zhí)行流程
Watcher機(jī)制主要包括客戶端線程、客戶端WatchManager和ZooKeeper服務(wù)器三部分。具體流程為:客戶端在向ZooKeeper服務(wù)器注冊Watcher的同時,會將Watcher對象存儲在客戶端的WatchManager 中。當(dāng)ZooKeeper服務(wù)器端觸發(fā)Watcher事件后,會向客戶端發(fā)送通知,客戶端線程從WatchManager中取出對應(yīng)的Watcher對象來執(zhí)行回調(diào)邏輯,如圖6-3所示。

圖6-3 ZooKeeper Watcher機(jī)制執(zhí)行流程
WatchManager類的部分源碼如下:

2. Watcher相關(guān)事件
我們可以調(diào)用exists()、getData()和getChildren()三個方法來設(shè)置Watcher,這些方法主要用于讀取ZooKeeper的狀態(tài)信息。下面列出了常用的設(shè)置Watcher事件的方法。
- 節(jié)點創(chuàng)建事件:通過調(diào)用exists()方法設(shè)置。
- 節(jié)點刪除事件:通過調(diào)用exists()、getData()和getChildren()方法設(shè)置。
- 節(jié)點改變事件:通過調(diào)用exists()和getData()方法設(shè)置。
- 子節(jié)點事件:通過調(diào)用getChildren()方法設(shè)置。
6.1.6 分布式鎖
在分布式環(huán)境中,為了保證在同一時刻只能有一個客戶端對指定的數(shù)據(jù)進(jìn)行訪問,需要使用分布式鎖技術(shù),只有獲得鎖的客戶端才能對數(shù)據(jù)進(jìn)行訪問,其余客戶端只能暫時等待。
利用ZooKeeper實現(xiàn)分布式鎖,常用的實現(xiàn)方法是,所有希望獲得鎖的客戶端都需要執(zhí)行以下操作:
(1)客戶端連接ZooKeeper,調(diào)用create()方法在指定的鎖節(jié)點(如/lock)下創(chuàng)建一個臨時順序節(jié)點。例如節(jié)點名為“node-”,則第一個客戶端創(chuàng)建的節(jié)點為“/lock/node-0000000000”,第二個客戶端創(chuàng)建的節(jié)點為“/lock/node-0000000001”。
(2)客戶端調(diào)用getChildren()方法查詢鎖節(jié)點/lock下的所有子節(jié)點列表,判斷子節(jié)點列表中序號最小的子節(jié)點是否是自己創(chuàng)建的。如果是,則客戶端獲得鎖,否則監(jiān)聽排在自己前一位的子節(jié)點的刪除事件,若監(jiān)聽的子節(jié)點被刪除,則重復(fù)執(zhí)行此步驟,直至獲得鎖。
(3)客戶端執(zhí)行業(yè)務(wù)代碼。
(4)客戶端業(yè)務(wù)完成后,刪除在ZooKeeper中對應(yīng)的子節(jié)點以釋放鎖。
針對上述流程中的兩個不容易理解的問題解析如下:
步驟(1)中為什么要創(chuàng)建臨時節(jié)點?
假如客戶端A獲得鎖之后,客戶端A所在的計算機(jī)宕機(jī)了,此時客戶端A沒有來得及主動刪除子節(jié)點。如果創(chuàng)建的是永久節(jié)點,鎖將永遠(yuǎn)不會被釋放,從而導(dǎo)致死鎖。臨時節(jié)點的好處是,盡管客戶端宕機(jī)了,但是ZooKeeper在一定時間內(nèi)沒有收到客戶端的心跳則會認(rèn)為會話失效,然后將臨時節(jié)點刪除以釋放鎖。
步驟(2)中未獲得鎖的客戶端為什么要監(jiān)聽排在自己前一位的子節(jié)點的刪除事件?
按照爭奪鎖的規(guī)則,每一輪鎖的爭奪取的都是序號最小節(jié)點,當(dāng)序號最小的節(jié)點刪除后,正常情況排在最小節(jié)點后一位的節(jié)點將獲得鎖,以此類推。因此,若客戶端沒有獲得鎖,只需要監(jiān)聽自己前一位的節(jié)點即可,這樣每當(dāng)鎖釋放時,ZooKeeper只需要通知一個客戶端,從而節(jié)省了網(wǎng)絡(luò)帶寬。若將監(jiān)聽事件設(shè)置在父節(jié)點/lock上,那么每次鎖的釋放將通知所有客戶端。假如客戶端數(shù)量龐大,會導(dǎo)致ZooKeeper服務(wù)器必須處理的操作數(shù)量激增,增加了ZooKeeper服務(wù)器的壓力,同時很容易產(chǎn)生網(wǎng)絡(luò)阻塞。
上述使用ZooKeeper實現(xiàn)分布式鎖的流程如圖6-4所示。

圖6-4 ZooKeeper 分布式鎖實現(xiàn)流程
- 計算機(jī)組成原理與接口技術(shù):基于MIPS架構(gòu)實驗教程(第2版)
- Building Computer Vision Projects with OpenCV 4 and C++
- Python數(shù)據(jù)分析與挖掘?qū)崙?zhàn)
- Test-Driven Development with Mockito
- Spark快速大數(shù)據(jù)分析(第2版)
- App+軟件+游戲+網(wǎng)站界面設(shè)計教程
- Visual Studio 2015 Cookbook(Second Edition)
- SQL Server 2008數(shù)據(jù)庫應(yīng)用技術(shù)(第二版)
- 深入淺出MySQL:數(shù)據(jù)庫開發(fā)、優(yōu)化與管理維護(hù)(第2版)
- 區(qū)塊鏈:看得見的信任
- 跟老男孩學(xué)Linux運(yùn)維:MySQL入門與提高實踐
- Oracle 12c云數(shù)據(jù)庫備份與恢復(fù)技術(shù)
- 金融商業(yè)算法建模:基于Python和SAS
- 數(shù)據(jù)科學(xué)工程實踐:用戶行為分析與建模、A/B實驗、SQLFlow
- INSTANT Apple iBooks How-to