一、What
- 一個主從架構(gòu)的分布式框架
- 給分布式框架提供協(xié)調(diào)服務(wù)(service)
作用
- 提供簡版文件系統(tǒng)來存儲數(shù)據(jù)
- 維護(hù)和監(jiān)控存儲的數(shù)據(jù)狀態(tài)變化,通過監(jiān)控數(shù)據(jù)狀態(tài)變化達(dá)到基于數(shù)據(jù)的集群管理
- 主要用來解決分布式集群中應(yīng)用系統(tǒng)的一致性問題
應(yīng)用場景
1. 主備切換
2. 節(jié)點(diǎn)的上下線感知
3. 統(tǒng)一命名服務(wù)
4. 狀態(tài)同步服務(wù)
5. 集群管理
6. 分布式應(yīng)用配置管理
二、基本概念
ZooKeeper=簡版文件系統(tǒng)(Znode)+原語+通知機(jī)制(Watcher)
- ZK文件系統(tǒng)
基于類似于文件系統(tǒng)的目錄節(jié)點(diǎn)樹方式的數(shù)據(jù)存儲 - 原語
提供類linux指令進(jìn)行操作 - Watcher(監(jiān)聽器)
數(shù)據(jù)節(jié)點(diǎn)ZNode
數(shù)據(jù)節(jié)點(diǎn)本質(zhì)就是目錄
| 持久節(jié)點(diǎn) | 臨時節(jié)點(diǎn) | |
|---|---|---|
| 非有序節(jié)點(diǎn) | create | create -e |
| 有序節(jié)點(diǎn) | create -s | create -s -e |
- 持久節(jié)點(diǎn)(無序)
節(jié)點(diǎn)創(chuàng)建以后,即便連接斷開,除非主動刪除,不然會一直存在 - 持久節(jié)點(diǎn)(有序)
創(chuàng)建節(jié)點(diǎn)的時候加上 -s ,會默認(rèn)的在目錄后加上數(shù)字
防止同一目錄創(chuàng)建同名ZNode導(dǎo)致失敗 - 臨時節(jié)點(diǎn)(無序)
節(jié)點(diǎn)創(chuàng)建以后,一旦連接斷開會自動刪除
創(chuàng)建節(jié)點(diǎn)的時候通過 -t 指定 - 持久節(jié)點(diǎn)(有序)
同上
會話
與zk交互時會建立TCP長連接,稱為會話
建立會話后,如果超過SessionTimeout時間,兩者間沒有通信,會話超時
特點(diǎn):同一個會話里執(zhí)行的指令是有序的;不同會話之間的指令是無序的
事務(wù)zxid
- 每個對數(shù)據(jù)的增刪改操作都會生成一個對應(yīng)的zxid
- zxid全局唯一,并且是自增的
- zxid通常是由64位數(shù)字,epoch+counter組成
Watch監(jiān)視與通知
方式一(輪詢):ZooKeeper以遠(yuǎn)程服務(wù)的方式,被客戶端訪問;客戶端以輪詢的方式獲得znode數(shù)據(jù),效率會比較低(代價比較大)
方式二(通知機(jī)制):客戶端在znode上注冊一個Watcher監(jiān)視器,當(dāng)znode上數(shù)據(jù)出現(xiàn)變化,watcher監(jiān)測到此變化,通知客戶端
- Watch作用流程
- 客戶端在服務(wù)器端,注冊的事件監(jiān)聽器
- watcher用于監(jiān)聽znode上的某些事件
- 比如znode數(shù)據(jù)修改、節(jié)點(diǎn)增刪等
- 當(dāng)監(jiān)聽到事件后,watcher會觸發(fā)通知客戶端
- 節(jié)點(diǎn)上下線原理
① 節(jié)點(diǎn)1(client1)創(chuàng)建臨時節(jié)點(diǎn)
② 節(jié)點(diǎn)2(client2)在臨時節(jié)點(diǎn),注冊監(jiān)聽器watcher
③ 當(dāng)client1與zk集群斷開連接,臨時節(jié)點(diǎn)會被刪除
④ watcher發(fā)送消息,通知client2,臨時節(jié)點(diǎn)被刪除的事件
用到的zk特性:Watcher+臨時節(jié)點(diǎn)
好處:通過這種方式,檢測和被檢測系統(tǒng)不需要直接關(guān)聯(lián)(如client1與client2),而是通過ZK上的某個節(jié)點(diǎn)進(jìn)行關(guān)聯(lián),大大減少了系統(tǒng)耦合。
三、HDFS HA方案
工作原理
ZooKeeper使用原子廣播協(xié)議叫做Zab(ZooKeeper Automic Broadcast)協(xié)議
- Zab協(xié)議有兩種模式
- 恢復(fù)模式(選主):因?yàn)閆ooKeeper是主從架構(gòu);當(dāng)ZooKeeper集群沒有主的角色leader時,從眾多服務(wù)器中選舉leader時,處于此模式
- 廣播模式(同步):當(dāng)集群有了leader后,客戶端向ZooKeeper集群讀寫數(shù)據(jù)時,集群處于此模式
- 為了保證事務(wù)的順序一致性,ZooKeeper采用了遞增的事務(wù)id號(zxid)來標(biāo)識事務(wù),所有提議(proposal)都有zxid
監(jiān)聽器
注冊:客戶端向ZooKeeper集群注冊監(jiān)聽器
監(jiān)聽事件:監(jiān)聽器負(fù)責(zé)監(jiān)聽特定的事件
回調(diào)函數(shù):當(dāng)監(jiān)聽器監(jiān)聽到事件的發(fā)生后,調(diào)用注冊監(jiān)聽器時定義的回調(diào)函數(shù)
HA原理
HDFS HA方案,主要分兩部分:
①元數(shù)據(jù)同步
- 在同一個HDFS集群,運(yùn)行兩個互為主備的NameNode節(jié)點(diǎn)。
- 一臺為主Namenode節(jié)點(diǎn),處于Active狀態(tài),一臺為備NameNode節(jié)點(diǎn),處于Standby狀態(tài)。
- 只有Active NameNode對外提供讀寫服務(wù),Standby NameNode會根據(jù)Active NameNode的狀態(tài)變化,在必要時切換成Active狀態(tài)。
-
JournalNode集群
- 在主備切換過程中,新的Active NameNode必須確保與原Active NamNode元數(shù)據(jù)同步完成,才能對外提供服務(wù)
- 所以用JournalNode集群作為共享存儲系統(tǒng);
- 當(dāng)客戶端對HDFS做操作,會在Active NameNode中edits.log文件中作日志記錄,同時日志記錄也會寫入JournalNode集群;負(fù)責(zé)存儲HDFS新產(chǎn)生的元數(shù)據(jù)
- 當(dāng)有新數(shù)據(jù)寫入JournalNode集群時,Standby NameNode能監(jiān)聽到此情況,將新數(shù)據(jù)同步過來
- Active NameNode(寫入)和Standby NameNode(讀取)實(shí)現(xiàn)元數(shù)據(jù)同步
- 另外,所有datanode會向兩個主備namenode做block report
②主備切換
- 每個NameNode節(jié)點(diǎn)上各有一個ZKFC進(jìn)程
- ZKFC即ZKFailoverController,作為獨(dú)立進(jìn)程存在,負(fù)責(zé)控制NameNode的主備切換
- ZKFC會監(jiān)控NameNode的健康狀況,當(dāng)發(fā)現(xiàn)Active NameNode異常時,通過Zookeeper集群進(jìn)行namenode主備選舉,完成Active和Standby狀態(tài)的切換
- ZKFC在啟動時,同時會初始化HealthMonitor和ActiveStandbyElector服務(wù)
- ZKFC同時會向HealthMonitor和ActiveStandbyElector注冊相應(yīng)的回調(diào)方法(如上圖的①回調(diào)、②回調(diào))
- HealthMonitor定時調(diào)用NameNode的HAServiceProtocol RPC接口(monitorHealth和getServiceStatus),監(jiān)控NameNode的健康狀態(tài),并向ZKFC反饋
- ActiveStandbyElector接收ZKFC的選舉請求,通過Zookeeper自動完成namenode主備選舉
- 選舉完成后回調(diào)ZKFC的主備切換方法對NameNode進(jìn)行Active和Standby狀態(tài)的切換
主備切換過程
- ① 啟動NameNode,ZKFC,此時兩個NameNode的狀態(tài)都是競選狀態(tài)
- ② 兩個ZKFC分別通過ActiveStandbyElector發(fā)起NameNode的選舉
通過zookeeper的寫一致性以及臨時節(jié)點(diǎn)來實(shí)現(xiàn) - ③ 發(fā)起主備選舉的時候,ActiveStandbyElector會嘗試在zookeeper的某個目錄下創(chuàng)建一個臨時節(jié)點(diǎn),zookeeper的寫一致性會保證只有一個節(jié)點(diǎn)創(chuàng)建成功
- ④ 創(chuàng)建成功的ActiveStandbyElector通過回調(diào)方式通知ZKFC,將對應(yīng)的NameNode切換為Active狀態(tài);創(chuàng)建失敗的也通過同樣方式將NameNode切換為Standby狀態(tài)
- ⑤ 無論是否創(chuàng)建成功,這些ActiveStandbyElector都會監(jiān)聽那個目錄;
當(dāng)Active NameNode對應(yīng)的HealthMonitor監(jiān)控到NameNode異常時,會告知ZKFC,ZKFC通過ActiveStandbyElector刪除所創(chuàng)建的臨時節(jié)點(diǎn),以及目錄A
若是ZKFC是因?yàn)楫惓嚅_的連接,那么目錄A還會存在 - ⑥ 此時處于Standby的NameNode會監(jiān)控到這個消息
它首先會通過判斷目錄A是否存在來確認(rèn)情況
如果是正常關(guān)閉的,則發(fā)起主備選舉,成功創(chuàng)建臨時節(jié)點(diǎn),并且將NameNode的狀態(tài)切換為Active
如果是異常關(guān)閉的,則會
1、通過RPC調(diào)用,試圖讓之前的Active的NameNode切換為StandBy
2、隔離:① 發(fā)送kill指令 ② 使用hadoop的隔離方式
腦裂
在分布式系統(tǒng)中雙主現(xiàn)象又稱為腦裂,由于Zookeeper的“假死”、長時間的垃圾回收或其它原因都可能導(dǎo)致雙Active NameNode現(xiàn)象,此時兩個NameNode都可以對外提供服務(wù),無法保證數(shù)據(jù)一致性
- 隔離
對于生產(chǎn)環(huán)境,這種情況的出現(xiàn)是毀滅性的,必須通過自帶的隔離(Fencing)機(jī)制預(yù)防此類情況 - 原理
ActiveStandbyElector成功創(chuàng)建ActiveStandbyElectorLock臨時節(jié)點(diǎn)后,額外創(chuàng)建一個ActiveBreadCrumb持久節(jié)點(diǎn)
ActiveBreadCurmb持久節(jié)點(diǎn)保存Active NameNode的服務(wù)器信息
當(dāng)Active NameNode正常狀態(tài)下斷開與Zookeeper Session,會一并刪除臨時節(jié)點(diǎn)ActiveStandbyElectorLock和ActiveBreadCurmb持久節(jié)點(diǎn)
-
如果是異常斷開的,那么此時臨時節(jié)點(diǎn)ActiveStandbyElectorLock不存在,但是ActiveBreadCurmb持久節(jié)點(diǎn)還存在;Standby節(jié)點(diǎn)會收到監(jiān)聽器發(fā)來的提醒將要從Standby切換成Active時,會先通過ActiveBreadCurmb里的服務(wù)器信息做隔離
1、通過RPC調(diào)用,試圖讓之前的Active的NameNode切換為StandBy
2、隔離(hadoop提供這兩種隔離):① 發(fā)送kill指令 ② 使用hadoop的隔離方式只有成功地fencing之后,選主成功的ActiveStandbyElector才會回調(diào)ZKFC的becomeActive方法transitionToActive將對應(yīng)的NameNode切換為Active,開始對外提供服務(wù)
四、Zookeeper架構(gòu)
ZooKeeper服務(wù)器四種狀態(tài):
looking:服務(wù)器處于尋找Leader群首的狀態(tài)
leading:服務(wù)器作為群首時的狀態(tài)
following:服務(wù)器作為follower跟隨者時的狀態(tài)
observing:服務(wù)器作為觀察者時的狀態(tài)
1、安其內(nèi)
全新leader選舉(重啟集群)
原則:集群超過半數(shù)的服務(wù)器啟動后,才能選出leader
選舉規(guī)則:
- 初始化:每個節(jié)點(diǎn)都投自己一票,然后向其他所有節(jié)點(diǎn)發(fā)送自己票的信息
- 交換投票信息:接收其他節(jié)點(diǎn)的選舉信息,與自己的那票進(jìn)行比較
投票信息vote信息結(jié)構(gòu)為(serverId, zxid)
會先比較zxid,zxid大的那臺服務(wù)器勝出(zxid越大意味存儲了更多的數(shù)據(jù))
若是zxid相同則serverId大的那臺服務(wù)器會勝出
比較結(jié)束后,每臺服務(wù)器會更新自己的投票信息,繼續(xù)給其他服務(wù)器發(fā)送 - 決定投票結(jié)果:假設(shè)服務(wù)器B接收到超過半數(shù)的票選舉自己,則自己從looking切換為leading,其他服務(wù)器從looking切換為following
- 當(dāng)服務(wù)器C啟動時,發(fā)現(xiàn)已有Leader,不再選舉,直接從Looking改為Following
全新leader選舉(leading掛掉或者出現(xiàn)網(wǎng)絡(luò)分區(qū))
攘其外
讀操作
- 常見的讀取操作,如ls /查看目錄;get /zktest查詢ZNode數(shù)據(jù)
- 讀操作
- 客戶端先與某個zk任意一臺服務(wù)器建立Session
- 然后,直接從此ZK服務(wù)器讀取數(shù)據(jù),并返回客戶端即可
- 關(guān)閉Session
寫操作
- ① 與zk任意一臺服務(wù)器建立Session建立連接
- ② follower將寫請求轉(zhuǎn)發(fā)給leader(不執(zhí)行)
- ③ leader收到消息后,發(fā)起proposal提案
- ④ 每臺服務(wù)器都會收到proposal后會記錄這次操作并向leader返回同意,但不執(zhí)行
- ⑤ 超過半數(shù)quorum同意,則leader提交commit提案,leader執(zhí)行該操作
- ⑥ leader通知所有節(jié)點(diǎn)也commit提交該提案;所有節(jié)點(diǎn)在所在服務(wù)器執(zhí)行該操作
- ⑦ client連接的那臺follower響應(yīng)Client
五、原理
quorum仲裁
- 什么是仲裁quorum?
- 發(fā)起proposal時,只要多數(shù)派同意,即可生效
- 為什么要仲裁?
- 多數(shù)據(jù)派不需要所有的服務(wù)器都響應(yīng),proposal就能生效
- 提高集群的響應(yīng)速度
- quorum數(shù)如何選擇?
- 集群節(jié)點(diǎn)數(shù) / 2 + 1
為什么集群節(jié)點(diǎn)數(shù)強(qiáng)烈建議奇數(shù)個?
5節(jié)點(diǎn)的比6節(jié)點(diǎn)的集群
- 容災(zāi)能力一樣,
- quorum小,響應(yīng)快
- 偶數(shù)個如果被網(wǎng)絡(luò)分區(qū)平分,則不能提供服務(wù)
網(wǎng)絡(luò)分區(qū)和腦裂
- 網(wǎng)絡(luò)分區(qū):網(wǎng)絡(luò)通信故障,集群被分成了2部分
- 腦裂:
- 原leader處于一個分區(qū);
- 另外一個分區(qū)選舉出新的leader
- 集群出現(xiàn)2個leader
ZAB算法
每個節(jié)點(diǎn)都有一個計時器(150毫秒~300毫秒之間的隨機(jī)數(shù)),時間到后會發(fā)起選舉的操作
非leader節(jié)點(diǎn)會定時的向leader節(jié)點(diǎn)發(fā)送心跳,leader收到心跳后會返回信息,非leader節(jié)點(diǎn)收到這個信息后會重置計時器
-
當(dāng)發(fā)生網(wǎng)絡(luò)分區(qū)并且leader處于少數(shù)的那邊時,其他follower會進(jìn)行選舉,假設(shè)B服務(wù)器選舉成功,那么它會和其他follower進(jìn)行zxid的比較
① 假設(shè)服務(wù)器C的zkid比服務(wù)器B的少10個,那么服務(wù)器B會將這十個操作封包發(fā)給服務(wù)器C來執(zhí)行
② 假設(shè)服務(wù)器D的zxid比服務(wù)器B多10個,那么服務(wù)器B會讓它將這十個記錄都刪除
做完以上操作后,服務(wù)器B才會從follower切換成leader,并且將epoch里的值進(jìn)行加1,zxid進(jìn)行重置 當(dāng)網(wǎng)絡(luò)恢復(fù)了之后,舊leader比較路徑下的epoch值的時候,發(fā)現(xiàn)已經(jīng)有新的leader產(chǎn)生,會將自己切換成follower,并且進(jìn)行數(shù)據(jù)的同步
狀態(tài)同步
完成選舉后,zk之間會進(jìn)行狀態(tài)同步操作
- leader構(gòu)建NEWLEADER封包,包含leader最大zxid值,廣播給其他follower
- follower收到后會跟自己最大zxid比較,若是比它小則進(jìn)行同步操作
- leader給每個需要同步的follower創(chuàng)建LearnerHandler同步線程,負(fù)責(zé)進(jìn)行同步操作
- leader主線程等待LearnerHandler線程處理結(jié)果
- 只有當(dāng)大部分follower完成同步,該集群才對外提供服務(wù),相應(yīng)寫請求
-
- LearnerHandler線程處理邏輯
- 接收follower封包FOLLOWERINFO,包含此follower最大zxid(代稱f-max-zxid)
- f-max-zxid與leader最大zxid(代稱l-max-zxid)比較
- 若相等,說明當(dāng)前follower是最新的
-
- 另外,若在判斷期間,有沒有新提交的proposal
- 如果有,那么會發(fā)送DIFF封包將有差異的數(shù)據(jù)同步過去.同時將follower沒有的數(shù)據(jù)逐個發(fā)送COMMIT封包給follower要求記錄下來.
- 如果follower數(shù)據(jù)id更大,那么會發(fā)送TRUNC封包告知截除多余數(shù)據(jù).
- 如果這一階段內(nèi)沒有提交的提議值,直接發(fā)送SNAP封包將快照同步發(fā)送給follower.
- 以上消息完畢之后,發(fā)送UPTODATE封包告知follower當(dāng)前數(shù)據(jù)就是最新的了
- 再次發(fā)送NEWLEADER封包宣稱自己是leader,等待follower的響應(yīng).
分布式鎖
- 所有需要獲取鎖的引用都在 /locker路徑下創(chuàng)建一個有序臨時節(jié)點(diǎn)
- 該路徑下序號最小的應(yīng)用獲取該鎖,其他應(yīng)用分別監(jiān)視比自己小一點(diǎn)的那個ZNode
- 當(dāng)序號最小的那個應(yīng)用操作完后,斷開連接時,比它稍微大一點(diǎn)的那個應(yīng)用會獲取鎖
分布式鎖主要是應(yīng)用了zk的一致性、臨時節(jié)點(diǎn)、watch監(jiān)視器這幾個特性來保證的
WHO
- NameNode使用ZooKeeper實(shí)現(xiàn)高可用.
- Yarn ResourceManager使用ZooKeeper實(shí)現(xiàn)高可用.
- 利用ZooKeeper對HBase集群做高可用配置
- kafka使用ZooKeeper(僅限0.9以及之前版本)
- 保存消息消費(fèi)信息比如offset.
- 用于檢測崩潰
- 主題topic發(fā)現(xiàn)
- 保持主題的生產(chǎn)和消費(fèi)狀態(tài)
常用操作
# 使用ZooKeeper自帶的腳本,連接ZooKeeper的服務(wù)器
zkCli.sh -server node01:2181,node02:2181,node03:2181
#創(chuàng)建節(jié)點(diǎn),并指定數(shù)據(jù)(必須要指定數(shù)據(jù))
create /kkb kkb