ZooKeeper 是一個分佈式的,開放源碼的分佈式應用程序協調服務,是 Google 的 Chubby 一個開源的實現,它是集羣的管理者,監視着集羣中各個節點的狀態根據節點提交的反饋進行下一步合理操做。最終,將簡單易用的接口和性能高效、功能穩定的系統提供給用戶。客戶端的讀請求能夠被集羣中的任意一臺機器處理,若是讀請求在節點上註冊了監聽器,這個監聽器也是由所鏈接的 zookeeper 機器來處理。對於寫請求,這些請求會同時發給其餘 zookeeper 機器而且達成一致後,請求才會返回成功。所以,隨着 zookeeper 的集羣機器增多,讀請求的吞吐會提升可是寫請求的吞吐會降低。有序性是 zookeeper 中很是重要的一個特性,全部的更新都是全局有序的,每一個更新都有一個惟一的時間戳,這個時間戳稱爲zxid(Zookeeper Transaction Id)。而讀請求只會相對於更新有序,也就是讀請求的返回結果中會帶有這個 zookeeper 最新的 zxidnode
一、文件系統nginx
二、通知機制面試
Zookeeper 提供一個多層級的節點命名空間(節點稱爲 znode)。與文件系統不一樣的是,這些節點均可以設置算法
關聯的數據,而文件系統中只有文件節點能夠存放數據而目錄節點不行。Zookeeper 爲了保證高吞吐和低延遲,在內存中維護了這個樹狀的目錄結構,這種特性使得 Zookeeper 不能用於存放大量的數據,每一個節點的存放數據上限爲 1M。數據庫
一、PERSISTENT-持久化目錄節點服務器
客戶端與 zookeeper 斷開鏈接後,該節點依舊存在網絡
二、PERSISTENT_SEQUENTIAL-持久化順序編號目錄節點負載均衡
客戶端與 zookeeper 斷開鏈接後,該節點依舊存在,只是 Zookeeper 給該節點名稱進行順序編號異步
三、EPHEMERAL-臨時目錄節點socket
客戶端與 zookeeper 斷開鏈接後,該節點被刪除
四、EPHEMERAL_SEQUENTIAL-臨時順序編號目錄節點
客戶端與 zookeeper 斷開鏈接後,該節點被刪除,只是 Zookeeper 給該節點名稱進行順序編號
client 端會對某個 znode 創建一個 watcher 事件,當該 znode 發生變化時,這些 client 會收到 zk 的通知,而後 client 能夠根據 znode 變化來作出業務上的改變等。
一、命名服務
二、配置管理
三、集羣管理
四、分佈式鎖
五、隊列管理
命名服務是指經過指定的名字來獲取資源或者服務的地址,利用 zk 建立一個全局的路徑,便是惟一的路徑,這個路徑就能夠做爲一個名字,指向集羣中的集羣,提供的服務的地址,或者一個遠程的對象等等。
程序分佈式的部署在不一樣的機器上,將程序的配置信息放在 zk 的 znode 下,當有配置發生改變時,也就是znode 發生變化時,能夠經過改變 zk 中某個目錄節點的內容,利用 watcher 通知給各個客戶端,從而更改配置。
所謂集羣管理無在意兩點:是否有機器退出和加入、選舉 master。
對於第一點,全部機器約定在父目錄下建立臨時目錄節點,而後監聽父目錄節點的子節點變化消息。一旦有機器掛掉,該機器與 zookeeper 的鏈接斷開,其所建立的臨時目錄節點被刪除,全部其餘機器都收到通知:某個兄弟目錄被刪除,因而,全部人都知道:它上船了。
新機器加入也是相似,全部機器收到通知:新兄弟目錄加入,highcount 又有了,對於第二點,咱們稍微改變一下,全部機器建立臨時順序編號目錄節點,每次選取編號最小的機器做爲 master 就好
有了 zookeeper 的一致性文件系統,鎖的問題變得容易。鎖服務能夠分爲兩類,一個是保持獨佔,另外一個是控制時序。
對於第一類,咱們將 zookeeper 上的一個 znode 看做是一把鎖,經過 createznode 的方式來實現。全部客戶端都去建立 /distribute_lock 節點,最終成功建立的那個客戶端也即擁有了這把鎖。用完刪除掉本身建立的distribute_lock 節點就釋放出鎖。
對於第二類, /distribute_lock 已經預先存在,全部客戶端在它下面建立臨時順序編號目錄節點,和選master 同樣,編號最小的得到鎖,用完刪除,依次方便。
在獲取分佈式鎖的時候在 locker 節點下建立臨時順序節點,釋放鎖的時候刪除該臨時節點。客戶端調用createNode 方法在 locker 下建立臨時順序節點,
而後調用 getChildren(「locker」)來獲取 locker 下面的全部子節點,注意此時不用設置任何 Watcher。客戶端獲取到全部的子節點 path 以後,若是發現本身建立的節點在全部建立的子節點序號最小,那麼就認爲該客戶端獲取到了鎖。若是發現本身建立的節點並不是 locker 全部子節點中最小的,說明本身尚未獲取到鎖,此時客戶端須要找到比本身小的那個節點,而後對其調用 exist()方法,同時對其註冊事件監聽器。以後,讓這個被關注的節點刪除,則客戶端的 Watcher 會收到相應通知,此時再次判斷本身建立的節點是不是 locker 子節點中序號最小的,若是是則獲取到了鎖,若是不是則重複以上步驟繼續獲取到比本身小的一個節點並註冊監聽。當前這個過程當中還須要許多的邏輯判斷。
代碼的實現主要是基於互斥鎖,獲取分佈式鎖的重點邏輯在於 BaseDistributedLock,實現了基於Zookeeper 實現分佈式鎖的細節。
兩種類型的隊列:
一、同步隊列,當一個隊列的成員都聚齊時,這個隊列纔可用,不然一直等待全部成員到達。二、隊列按照 FIFO 方式進行入隊和出隊操做。
第一類,在約定目錄下建立臨時目錄節點,監聽節點數目是不是咱們要求的數目。
第二類,和分佈式鎖服務中的控制時序場景基本原理一致,入列有編號,出列按編號。在特定的目錄下建立PERSISTENT_SEQUENTIAL 節點,建立成功時 Watcher 通知等待的隊列,隊列刪除序列號最小的節點用以消費。此場景下 Zookeeper 的 znode 用於消息存儲,znode 存儲的數據就是消息隊列中的消息內容,SEQUENTIAL 序列號就是消息的編號,按序取出便可。因爲建立的節點是持久化的,因此沒必要擔憂隊列消息的丟失問題。
Zookeeper 做爲一個集羣提供一致的數據服務,天然,它要在全部機器間作數據複製。
數據複製的好處:
一、容錯:一個節點出錯,不致於讓整個系統中止工做,別的節點能夠接管它的工做;
二、提升系統的擴展能力 :把負載分佈到多個節點上,或者增長節點來提升系統的負載能力;
三、提升性能:讓客戶端本地訪問就近的節點,提升用戶訪問速度。
從客戶端讀寫訪問的透明度來看,數據複製集羣系統分下面兩種:
一、寫主(WriteMaster) :對數據的修改提交給指定的節點。讀無此限制,能夠讀取任何一個節點。這種狀況下客戶端須要對讀與寫進行區別,俗稱讀寫分離;
二、寫任意(Write Any):對數據的修改可提交給任意的節點,跟讀同樣。這種狀況下,客戶端對集羣節點的角色與變化透明。
對 zookeeper 來講,它採用的方式是寫任意。經過增長機器,它的讀吞吐能力和響應能力擴展性很是好,而寫,隨着機器的增多吞吐能力確定降低(這也是它創建 observer 的緣由),而響應能力則取決於具體實現方式,是延遲複製保持最終一致性,仍是當即複製快速響應。
Zookeeper 的核心是原子廣播,這個機制保證了各個 Server 之間的同步。實現這個機制的協議叫作 Zab 協議。Zab 協議有兩種模式,它們分別是恢復模式(選主)和廣播模式(同步)。當服務啓動或者在領導者崩潰後,Zab 就進入了恢復模式,當領導者被選舉出來,且大多數 Server 完成了和 leader 的狀態同步之後,恢復模式就結束了。狀態同步保證了 leader 和 Server 具備相同的系統狀態。
zookeeper 採用了遞增的事務 Id 來標識,全部的 proposal(提議)都在被提出的時候加上了 zxid,zxid 其實是一個 64 位的數字,高 32 位是 epoch(時期; 紀元; 世; 新時代)用來標識 leader 是否發生改變,若是有新的 leader 產生出來,epoch 會自增,低 32 位用來遞增計數。當新產生 proposal 的時候,會依據數據庫的兩階段過程,首先會向其餘的 server 發出事務執行請求,若是超過半數的機器都能執行而且可以成功,那麼就會開始執行。
每一個 Server 在工做過程當中有三種狀態:
LOOKING:當前 Server 不知道 leader 是誰,正在搜尋
LEADING:當前 Server 即爲選舉出來的
leaderFOLLOWING:leader 已經選舉出來,當前 Server 與之同步
當 leader 崩潰或者 leader 失去大多數的 follower,這時 zk 進入恢復模式,恢復模式須要從新選舉出一個新的leader,讓全部的 Server 都恢復到一個正確的狀態。Zk 的選舉算法有兩種:一種是基於 basic paxos 實現的,另一種是基於 fast paxos 算法實現的。系統默認的選舉算法爲 fast paxos。
一、Zookeeper 選主流程(basic paxos)
(1)選舉線程由當前 Server 發起選舉的線程擔任,其主要功能是對投票結果進行統計,並選出推薦的Server;
(2)選舉線程首先向全部 Server 發起一次詢問(包括本身);
(3)選舉線程收到回覆後,驗證是不是本身發起的詢問(驗證 zxid 是否一致),而後獲取對方的 id(myid),並存儲到當前詢問對象列表中,最後獲取對方提議的 leader 相關信息(id,zxid),並將這些信息存儲到當次選舉的投票記錄表中;
(4)收到全部 Server 回覆之後,就計算出 zxid 最大的那個 Server,並將這個 Server 相關信息設置成下一次要投票的 Server;
(5)線程將當前 zxid 最大的 Server 設置爲當前 Server 要推薦的 Leader,若是此時獲勝的 Server 得到 n/2+ 1 的 Server 票數,設置當前推薦的 leader 爲獲勝的 Server,將根據獲勝的 Server 相關信息設置本身的狀態,不然,繼續這個過程,直到 leader 被選舉出來。 經過流程分析咱們能夠得出:要使 Leader 得到多數Server 的支持,則 Server 總數必須是奇數 2n+1,且存活的 Server 的數目不得少於 n+1. 每一個 Server 啓動後都會重複以上流程。在恢復模式下,若是是剛從崩潰狀態恢復的或者剛啓動的 server 還會從磁盤快照中恢復數據和會話信息,zk 會記錄事務日誌並按期進行快照,方便在恢復時進行狀態恢復
二、Zookeeper 選主流程(basic paxos)
fast paxos 流程是在選舉過程當中,某 Server 首先向全部 Server 提議本身要成爲 leader,當其它 Server 收到提議之後,解決 epoch 和 zxid 的衝突,並接受對方的提議,而後向對方發送接受提議完成的消息,重複這個流程,最後必定能選舉出 Leader。
選完 Leader 之後,zk 就進入狀態同步過程。
一、Leader 等待 server 鏈接;
二、Follower 鏈接 leader,將最大的 zxid 發送給 leader;
三、Leader 根據 follower 的 zxid 肯定同步點;
四、完成同步後通知 follower 已經成爲 uptodate 狀態;
五、Follower 收到 uptodate 消息後,又能夠從新接受 client 的請求進行服務了。
對於系統調度來講:操做人員發送通知實際是經過控制檯改變某個節點的狀態,而後 zk 將這些變化發送給註冊了這個節點的 watcher 的全部客戶端。
對於執行狀況彙報:每一個工做進程都在某個目錄下建立一個臨時節點。並攜帶工做的進度數據,這樣彙總的進程能夠監控目錄子節點的變化得到工做進度的實時的全局狀況。
在分佈式環境中,有些業務邏輯只須要集羣中的某一臺機器進行執行,其餘的機器能夠共享這個結果,這樣能夠大大減小重複計算,提升性能,因而就須要進行 leader 選舉。
Zookeeper 自己也是集羣,推薦配置很多於 3 個服務器。Zookeeper 自身也要保證當一個節點宕機時,其餘節點會繼續提供服務。
若是是一個 Follower 宕機,還有 2 臺服務器提供訪問,由於 Zookeeper 上的數據是有多個副本的,數據並不會丟失;
若是是一個 Leader 宕機,Zookeeper 會選舉出新的 Leader。
ZK 集羣的機制是隻要超過半數的節點正常,集羣就能正常提供服務。只有在 ZK 節點掛得太多,只剩一半或不到一半節點能工做,集羣才失效。
因此
3 個節點的 cluster 能夠掛掉 1 個節點(leader 能夠獲得 2 票>1.5)
2 個節點的 cluster 就不能掛掉任何 1 個節點了(leader 能夠獲得 1 票<=1)
zk 的負載均衡是能夠調控,nginx 只是能調權重,其餘須要可控的都須要本身寫插件;可是 nginx 的吞吐量比zk 大不少,應該說按業務選擇用哪一種方式。
Watch 機制官方聲明:一個 Watch 事件是一個一次性的觸發器,當被設置了 Watch 的數據發生了改變的時候,則服務器將這個改變發送給設置了 Watch 的客戶端,以便通知它們。Zookeeper 機制的特色:
一、一次性觸發數據發生改變時,一個 watcher event 會被髮送到 client,可是 client 只會收到一次這樣的信息。
二、watcher event 異步發送 watcher 的通知事件從 server 發送到 client 是異步的,這就存在一個問題,不一樣的客戶端和服務器之間經過 socket 進行通訊,因爲網絡延遲或其餘因素致使客戶端在不通的時刻監聽到事件,因爲 Zookeeper 自己提供了 ordering guarantee,即客戶端監聽事件後,纔會感知它所監視 znode 發生了變化。因此咱們使用 Zookeeper 不能指望可以監控到節點每次的變化。Zookeeper 只能保證最終的一致性,而沒法保證強一致性。
三、數據監視 Zookeeper 有數據監視和子數據監視 getdata() and exists()設置數據監視,getchildren()設置了子節點監視。
四、註冊 watcher getData、exists、getChildren
五、觸發 watcher create、delete、setData
六、setData()會觸發 znode 上設置的 data watch(若是 set 成功的話)。一個成功的 create() 操做會觸發建立的 znode 上的數據 watch,以及其父節點上的 child watch。而一個成功的 delete()操做將會同時觸發一個 znode 的 data watch 和 child watch(由於這樣就沒有子節點了),同時也會觸發其父節點的 childwatch。
七、當一個客戶端鏈接到一個新的服務器上時,watch 將會被以任意會話事件觸發。當與一個服務器失去鏈接的時候,是沒法接收到 watch 的。而當 client 從新鏈接時,若是須要的話,全部先前註冊過的 watch,都會被從新註冊。一般這是徹底透明的。只有在一個特殊狀況下,watch 可能會丟失:對於一個未建立的 znode 的exist watch,若是在客戶端斷開鏈接期間被建立了,而且隨後在客戶端鏈接上以前又刪除了,這種狀況下,這個 watch 事件可能會被丟失。
八、Watch 是輕量級的,其實就是本地 JVM 的 Callback,服務器端只是存了是否有設置了 Watcher 的布爾類型
文末分享:有須要學習資料的小夥伴能夠加Qun:1017-599-436 免費獲取視頻資料和麪試文檔資料!