基本概念
ZAB協議
客戶端腳本
javaAPI
ZKClient & Curator
應用場景
數據發布訂閱
負載均衡
命名服務
分布式協調/通知
集群管理
Master選舉
分布式鎖
分布式隊列
使用優化
(一)分布式基礎知識
- 分布式的特點:分布性、對等性、并發性、缺乏全局時鐘、故障總會發生
- 分布式環境下的各種問題:通訊異常、網絡分區、成功失敗超時三態、節點故障
- 從ACID到BASE的變化
- 一致性協議:
- 2pc(請求處理-->提交確認)與3pc(事務處理能力詢問-->處理后待提交-->提交確認)的特點及優缺點比較
- Paxos協議的原理及在Chubby、Hypertable上的實踐。
(二)Zookeeper可以保證的分布式一致性特性:
- 順序一致性:同一個客戶端發起的事務請求,嚴格按其順序處理
- 原子性:事務請求處理的原子性
- 單一視圖:無論客戶端連接到哪個服務器,看到的數據模型是一致的
- 可靠性:對事務的處理完成并反饋后,狀態會一致保留直到被其他事務改變
- 實時性:一定時間段內,客戶端最終一定能從服務器上讀取到最新的數據狀態
(三)Zookeeper中的核心概念/特性:
全部存儲在內存中的樹形數據節點ZNode,分為持久(順序)型與臨時(順序)節點(生命周期與客戶端會話綁定),每個ZNode只能由一臺服務器創建,且節點的sequential自增數字保障兄弟節點按順序無重復
-
三種集群角色
- Leader:處理事務請求并保證事務請求的順序性(事務指能夠改變Zookeeper服務狀態的操作,一般包括數據節點的創建刪除與內容更新、客戶端會話創建與失效。每一個事務有全局唯一的ZXID);集群內部各服務器的調度
- Follower:處理客戶端非事務請求、轉發事務請求給Leader、參與事務請求Proposal投票、參與Leader選舉投票
- Observer:只處理非事務服務,不參與任何形式的投票
Stat數據結構維護當前ZNode的三個數據版本:當前版本version、當前子節點版本cversion、ACL版本aversion
客戶端與服務端TCP長連接的會話Session管理,分桶管理策略通過設定固定周期的超時檢查,批量清理超時會話。
客戶端利用API可對數據節點進行如下操作:創建會話、創建節點、刪除節點、讀取數據、更新數據、檢測節點是否存在、權限控制。
常用開源的兩款zookeeper客戶端:ZkClient、Curator。
Watcher事件監聽:客戶端通過監聽特點節點上的特定事件,實現分布式協調服務
ACL:類似于UNIX文件系統的權限控制,包括增、刪、讀、寫、admin設置等五種權限
Jute為序列化組件
Zookeeper將所有節點的路徑、數據內容、ACL等信息組成DataTree全部存儲在內存中,其底層數據結構是ConcurrentHashMap,其key是數據節點的path,而value則是真正的數據內容DataNode。
通過ZKDatabase管理所有會話、DataTree存儲和事務日志,并定時dump快照到磁盤,同時也方便在啟動時從磁盤上的事務日志和快照數據文件恢復成一個完整的內存數據庫
說明:ZXID是一個64位的數字,其中低32位針對客戶端每一個事務請求,Leader服務器在產生一個新的事務proposal時進行+1,高32位代表本輪Leader周期的epoch標號,當新選舉一個Leader后會對此epoch+1以區分出Leader周期的變化。集群中擁有XZID最大的proposal的機器會成為Leader,因為它一定具有所有已經提交的提案。
(四)Zookeeper的核心協議ZAB:
主備模型架構保證同一時刻只有一個主進程處理事務請求并廣播狀態,并能保證全局的變更系列被順序應用;
所有事務請求必須由一個全局唯一的服務器Leader來協調處理,其他事務請求按照類似兩階段提交的過程向Follower服務器發送proposal提議;
-
ZAB協議包括兩種基本模式:崩潰恢復和消息廣播
- 崩潰恢復:當服務啟動、Leader網絡中斷或退出、重啟等進去恢復模式,選舉產生Leader并在過半的機器從該Leader中國完成了數據同步后退出此模式進行消息廣播模式并提供服務,因此只要集群中存在過半的服務器能夠彼此進行正常通信,就可以選舉出新的Leader并再次進入消息廣播模式;
- 消息廣播:Leader為每個事物請求生成ZXID的事務proposal廣播給Follower,Follower接收到后以事務日志的形式寫入本地磁盤并ACK響應,當Leader接收到過半Follower的ACK后,廣播一個commit消息給所有的Follower通知事務提交,同時自身提交,Follower接到commit后也完成提交從而完成這個事務處理。
(五)ZAB協議與Paxos算法的區別于聯系:
聯系:都有Leader、Follower、epoch值;
-
區別:
- Paxos在新選舉的主進程中會進行兩階段的工作,第一階段讀階段主進程通過和其他進程的通信來收集上一個主進程的提案并提交;第二階段為寫階段即新主進程開始提出自己的提案;
- ZAB協議在讀階段之后額外引入一個同步階段,在此階段中新的Leader會確保存在過半的Follower已經提交了之前Leader周期中所有的事務proposal,從而保證在新Leader提交proposal之前,所有的Follower都完成了對之前所有事務proposal的提交。同步階段完成后再進入寫階段。
(六)ZooKeeper典型8大應用場景及對應的特性:
- 數據發布訂閱:對統一配置信息等數據可以通過在Zookeeper創建一個數據節點并讓客戶端進行監聽,主要利用了Zookeeper的Watcher監聽特性;
- 負載均衡:創建一個節點,負載應用把自己的服務地址寫到此節點下,如果此應用掛掉,則此子節點消失
- 命名服務:利用Zookeeper創建順序無重復子節點的特性;
- 分布式協調/通知:不同客戶端都對Zookeeper上的同一個數據節點進行watcher注冊,監聽數據節點的變化,當發生變化所有訂閱的客戶端接收到通知并進行處理;
- 集群管理:利用了watcher監聽與臨時節點在會話失效自動清除的特性。同時,各服務器可以講運行狀態信息寫入到臨時節點中進而有助于Leader收集負載信息;
- Master選舉:所有客戶端創建同一個path的數據節點,只有一個能成功,即為Master;
- 分布式鎖:創建臨時節點,誰成功即獲得鎖。另外,根據創建時不同的類型-序號,根據一定的規則可以模擬出共享鎖、讀寫鎖等;
- 分布式隊列:每個客戶端在指定節點下創建臨時節點,然后獲取該指定節點下的所有子節點并判斷自己是否是序號最小的節點,如果是則可以進行處理,如果不是則進入等待并監聽比自己序號小的最后一個節點,待接到watcher通知后,重復檢查。
相關問題
ZK選舉過程
當leader崩潰或者leader失去大多數的follower,這時候zk進入恢復模式,恢復模式需要重新選舉出一個新的leader,讓所有的Server都恢復到一個正確的狀態。Zk的選舉算法使用ZAB協議:
- 選舉線程由當前Server發起選舉的線程擔任,其主要功能是對投票結果進行統計,并選出推薦的Server;
- 選舉線程首先向所有Server發起一次詢問(包括自己);
- 選舉線程收到回復后,驗證是否是自己發起的詢問(驗證zxid是否一致),然后獲取對方的id(myid),并存儲到當前詢問對象列表中,最后獲取對方提議的leader相關信息(id,zxid),并將這些信息存儲到當次選舉的投票記錄表中;
- 收到所有Server回復以后,就計算出zxid最大的那個Server,并將這個Server相關信息設置成下一次要投票的Server;
- 線程將當前zxid最大的Server設置為當前Server要推薦的Leader,如果此時獲勝的Server獲得n/2 + 1的Server票數, 設置當前推薦的leader為獲勝的Server,將根據獲勝的Server相關信息設置自己的狀態,否則,繼續這個過程,直到leader被選舉出來。
通過流程分析我們可以得出:要使Leader獲得多數Server的支持,則Server總數最好是奇數2n+1,且存活的Server的數目不得少于n+1
master/slave之間通信
Storm:定期掃描
PtBalancer:節點監聽
節點變多時,PtBalancer速度變慢
原因有五:
- ZK有1MB 的傳輸限制。 實踐中ZNode必須相對較小,而隊列包含成千上萬的消息,非常的大。
- 如果有很多節點,ZK啟動時相當的慢。 而使用queue會導致好多ZNode. 你需要顯著增大 initLimit 和 syncLimit.
- Node很大的時候很難清理。Netflix不得不創建了一個專門的程序做這事。
- 當很大量的包含成千上萬的子節點的ZNode時, ZK的性能變得不好
- ZK的數據庫完全放在內存中。 大量的Queue意味著會占用很多的內存空間。
盡管如此, Curator還是創建了各種Queue的實現。 如果Queue的數據量不太多,數據量不太大的情況下,酌情考慮,還是可以使用的。
客戶端對ServerList的輪詢機制是什么
隨機,客戶端在初始化( new ZooKeeper(String connectString, int sessionTimeout, Watcher watcher) )的過程中,將所有Server保存在一個List中,然后**隨機打散,形成一個環。之后從0號位開始輪訓使用。 **
兩個注意點:
- Server地址能夠重復配置,這樣能夠彌補客戶端無法設置Server權重的缺陷,但是也會加大風險。(比如: 192.168.1.1:2181,192.168.1.1:2181,192.168.1.2:2181).
- 如果客戶端在進行Server切換過程中耗時過長,那么將會收到SESSION_EXPIRED. 這也是上面第1點中的加大風險之處。
客戶端如何正確處理CONNECTIONLOSS(連接斷開) 和 SESSIONEXPIRED(Session 過期)兩類連接異常
- 在ZooKeeper中,服務器和客戶端之間維持的是一個長連接
- 在 SESSION_TIMEOUT 時間內,服務器會確定客戶端是否正常連接(客戶端會定時向服務器發送heart_beat),服務器重置下次SESSION_TIMEOUT時間。
- 在正常情況下,Session一直有效,并且zk集群所有機器上都保存這個Session信息。
- 在出現問題情況下,客戶端與服務器之間連接斷了(客戶端所連接的那臺zk機器掛了,或是其它原因的網絡閃斷),這個時候客戶端會主動在地址列表(初始化的時候傳入構造方法的那個參數connectString)中選擇新的地址進行連接。
好了,上面基本就是服務器與客戶端之間維持長連接的過程了。在這個過程中,用戶可能會看到兩類異常CONNECTIONLOSS(連接斷開) 和SESSIONEXPIRED(Session 過期)。
CONNECTIONLOSS發生在上面紅色文字部分,應用在進行操作A時,發生了CONNECTIONLOSS,此時用戶不需要關心我的會話是否可用,應用所要做的就是等待客戶端幫我們自動連接上新的zk機器,一旦成功連接上新的zk機器后,確認剛剛的操作A是否執行成功了。
一個客戶端修改了某個節點的數據,其它客戶端能夠馬上獲取到這個最新數據嗎
- ZooKeeper不能確保任何客戶端能夠獲?。碦ead Request)到一樣的數據(*最終一致)
- 除非客戶端自己要求:方法是客戶端在獲取數據之前調用org.apache.zookeeper.AsyncCallback.VoidCallback, java.lang.Object) sync.
- 通常情況下(這里所說的通常情況滿足:1. 對獲取的數據是否是最新版本不敏感,2. 一個客戶端修改了數據,其它客戶端是否需要立即能夠獲取最新),可以不關心這點。
- 在其它情況下,最清晰的場景是這樣:ZK客戶端A對 /my_test 的內容從 v1->v2, 但是ZK客戶端B對 /my_test 的內容獲取,依然得到的是 v1. 請注意,這個是實際存在的現象,當然延時很短。解決的方法是客戶端B先調用 sync(), 再調用 getData().
ZK為什么不提供一個永久性的Watcher注冊機制
不支持用持久Watcher的原因很簡單,永久性的Watcher很費資源,占用帶寬,(相當于推模式,推模式的弊端),而是客戶端用的時候注冊拉去。
使用watch需要注意的幾點
- Watches通知是一次性的,必須重復注冊.
- 發生CONNECTIONLOSS之后,只要在session_timeout之內再次連接上(即不發生SESSIONEXPIRED),那么這個連接注冊的watches依然在。
- 節點數據的版本變化會觸發NodeDataChanged,注意,這里特意說明了是版本變化。存在這樣的情況,只要成功執行了setData()方法,無論內容是否和之前一致,都會觸發NodeDataChanged。
- 對某個節點注冊了watch,但是節點被刪除了,那么注冊在這個節點上的watches都會被移除。
- 同一個zk客戶端對某一個節點注冊相同的watch,只會收到一次通知。
- Watcher對象只會保存在客戶端,不會傳遞到服務端。
能否收到每次節點變化的通知
如果節點數據的更新頻率很高的話,不能!!!
原因在于:當一次數據修改,通知客戶端,客戶端再次注冊watch,在這個過程中,可能數據已經發生了許多次數據修改,因此,千萬不要做這樣的測試:”數據被修改了n次,一定會收到n次通知”來測試server是否正常工作。(我曾經就做過這樣的傻事,發現Server一直工作不正常?其實不是)。即使你使用了GitHub上這個客戶端也一樣。
能為臨時節點創建子節點嗎
不能。
是否可以拒絕單個IP對ZK的訪問和操作
ZK本身不提供這樣的功能,它僅僅提供了對單個IP的連接數的限制。你可以通過修改iptables來實現對單個ip的限制
在getChildren(String path, boolean watch)是注冊了對節點子節點的變化,那么子節點的子節點變化能通知嗎
不能
創建的臨時節點什么時候會被刪除,是連接一斷就刪除嗎?延時是多少?
連接斷了之后,ZK不會馬上移除臨時數據,只有當SESSIONEXPIRED之后,才會把這個會話建立的臨時數據移除。
因此,用戶需要謹慎設置Session_TimeOut
zookeeper是否支持動態進行機器擴容?如果目前不支持,那么要如何擴容呢?
截止3.4.3版本的zookeeper,還不支持這個功能,在3.5.0版本開始,支持動態加機器了,期待下吧: https://issues.apache.org/jira/browse/ZOOKEEPER-107
ZooKeeper集群中服務器之間是怎樣通信的?
Leader服務器會和每一個Follower/Observer服務器都建立TCP連接,同時為每個F/O都創建一個叫做LearnerHandler的實體。LearnerHandler主要負責Leader和F/O之間的網絡通訊,包括數據同步,請求轉發和Proposal提議的投票等。Leader服務器保存了所有F/O的LearnerHandler。
zookeeper是否會自動進行日志清理?如何進行日志清理?
zk自己不會進行日志清理,需要運維人員進行日志清理
Ref:
《從Paxos到zookeeper分布式一致性原理與實踐》
http://www.cnblogs.com/dimmacro/p/4457665.html