Zookeeper是一個高性能,分布式的,開源分布式應用協調服務。它提供了簡單原始的功能,分布式應用可以基於它實現更高級的服務,比如同步,配置管理,集群管理,名空間。它被設計為易於編程,使用文件系統目錄樹作為數據模型。服務端跑在java上,提供java和C的客戶端API。Zookeeper是Google的Chubby一個開源的實現,是高有效和可靠的協同工作系統,Zookeeper能夠用來leader選舉,配置信息維護等,在一個分布式的環境中,需要一個Master實例或存儲一些配置信息,確保文件寫入的一致性等。
Zookeeper總體結構
Zookeeper服務自身組成一個集群(2n+1個服務允許n個失效)。Zookeeper服務有兩個角色,一個是leader,負責寫服務和數據同步,剩下的是follower,提供讀服務,leader失效后會在follower中重新選舉新的leader。
Zookeeper邏輯圖如下:
- 客戶端可以連接到每個server,每個server的數據完全相同。
- 每個follower都和leader有連接,接受leader的數據更新操作。
- Server記錄事務日志和快照到持久存儲。
- 大多數server可用,整體服務就可用。
ZooKeeper的基本運轉流程:
- 選舉Leader。
- 同步數據。
- 選舉Leader過程中算法有很多,但要達到的選舉標准是一致的。
- Leader要具有最高的zxid。
- 集群中大多數的機器得到響應並follow選出的Leader。
Zookeeper表現為一個分層的文件系統目錄樹結構(不同於文件系統的是,節點可以有自己的數據,而文件系統中的目錄節點只有子節點)。數據模型結構圖如下:
圓形節點可以含有子節點,多邊形節點不能含有子節點。一個節點對應一個應用,節點存儲的數據就是應用需要的配置信息。
Zookeeper 特點
- 順序一致性:按照客戶端發送請求的順序更新數據。
- 原子性:更新要么成功,要么失敗,不會出現部分更新。
- 單一性 :無論客戶端連接哪個server,都會看到同一個視圖。
- 可靠性:一旦數據更新成功,將一直保持,直到新的更新。
- 及時性:客戶端會在一個確定的時間內得到最新的數據。
Zookeeper利於分布式系統開發,它能讓分布式系統更加健壯和高效。它的主要優點有:
- zookeeper是一個精簡的文件系統。這點它和hadoop有點像,但是zookeeper這個文件系統是管理小文件的,而hadoop是管理超大文件的。
- zookeeper提供了豐富的“構件”,這些構件可以實現很多協調數據結構和協議的操作。例如:分布式隊列、分布式鎖以及一組同級節點的“領導者選舉”算法。
- zookeeper是高可用的,它本身的穩定性是相當之好,分布式集群完全可以依賴zookeeper集群的管理,利用zookeeper避免分布式系統的單點故障的問題。
- zookeeper采用了松耦合的交互模式。這點在zookeeper提供分布式鎖上表現最為明顯,zookeeper可以被用作一個約會機制,讓參入的進程不在了解其他進程的(或網絡)的情況下能夠彼此發現並進行交互,參入的各方甚至不必同時存在,只要在zookeeper留下一條消息,在該進程結束后,另外一個進程還可以讀取這條信息,從而解耦了各個節點之間的關系。
- zookeeper為集群提供了一個共享存儲庫,集群可以從這里集中讀寫共享的信息,避免了每個節點的共享操作編程,減輕了分布式系統的開發難度。
- zookeeper的設計采用的是觀察者的設計模式,zookeeper主要是負責存儲和管理大家關心的數據,然后接受觀察者的注冊,一旦這些數據的狀態發生變化,Zookeeper 就將負責通知已經在 Zookeeper 上注冊的那些觀察者做出相應的反應,從而實現集群中類似 Master/Slave 管理模式。
Zookeeper 會維護一個具有層次關系的數據結構,它非常類似於一個標准的文件系統,如圖所示:
Zookeeper 這種數據結構有如下這些特點:
- 每個子目錄項如 NameService 都被稱作為 znode,這個 znode 是被它所在的路徑唯一標識,如 Server1 這個 znode 的標識為 /NameService/Server1
- znode 可以有子節點目錄,並且每個 znode 可以存儲數據,注意 EPHEMERAL 類型的目錄節點不能有子節點目錄
- znode 是有版本的,每個 znode 中存儲的數據可以有多個版本,也就是一個訪問路徑中可以存儲多份數據
- znode 可以是臨時節點,一旦創建這個 znode 的客戶端與服務器失去聯系,這個 znode 也將自動刪除,Zookeeper 的客戶端和服務器通信采用長連接方式,每個客戶端和服務器通過心跳來保持連接,這個連接狀態稱為 session,如果 znode 是臨時節點,這個 session 失效,znode 也就刪除了
- znode 的目錄名可以自動編號,如 App1 已經存在,再創建的話,將會自動命名為 App2
- znode 可以被監控,包括這個目錄節點中存儲的數據的修改,子節點目錄的變化等,一旦變化可以通知設置監控的客戶端,這個是 Zookeeper 的核心特性,Zookeeper 的很多功能都是基於這個特性實現的,后面在典型的應用場景中會有實例介紹
四種類型的znode:
- PERSISTENT-持久化目錄節點。客戶端與zookeeper斷開連接后,該節點依舊存在
- PERSISTENT_SEQUENTIAL-持久化順序編號目錄節點。客戶端與zookeeper斷開連接后,該節點依舊存在,只是Zookeeper給該節點名稱進行順序編號
- EPHEMERAL-臨時目錄節點。客戶端與zookeeper斷開連接后,該節點被刪除
- EPHEMERAL_SEQUENTIAL-臨時順序編號目錄節點。客戶端與zookeeper斷開連接后,該節點被刪除,只是Zookeeper給該節點名稱進行順序編號
ZooKeeper Client Library提供了豐富直觀的API供用戶程序使用,下面是一些常用的API:
- create(path, data, flags): 創建一個ZNode, path是其路徑,data是要存儲在該ZNode上的數據,flags常用的有: PERSISTEN, PERSISTENT_SEQUENTAIL, EPHEMERAL, EPHEMERAL_SEQUENTAIL
- delete(path, version): 刪除一個ZNode,可以通過version刪除指定的版本, 如果version是-1的話,表示刪除所有的版本
- exists(path, watch): 判斷指定ZNode是否存在,並設置是否Watch這個ZNode。這里如果要設置Watcher的話,Watcher是在創建ZooKeeper實例時指定的,如果要設置特定的Watcher的話,可以調用另一個重載版本的exists(path, watcher)。以下幾個帶watch參數的API也都類似
- getData(path, watch): 讀取指定ZNode上的數據,並設置是否watch這個ZNode
- setData(path, watch): 更新指定ZNode的數據,並設置是否Watch這個ZNode
- getChildren(path, watch): 獲取指定ZNode的所有子ZNode的名字,並設置是否Watch這個ZNode
- sync(path): 把所有在sync之前的更新操作都進行同步,達到每個請求都在半數以上的ZooKeeper Server上生效。path參數目前沒有用
- setAcl(path, acl): 設置指定ZNode的Acl信息
- getAcl(path): 獲取指定ZNode的Acl信息
Zookeeper的應用場景:
1、命名服務
命名服務也是分布式系統中比較常見的一類場景。在分布式系統中,通過使用命名服務,客戶端應用能夠根據指定名字來獲取資源或服務的地址,提供者等信息。被命名的實體通常可以是集群中的機器,提供的服務地址,遠程對象等等——這些我們都可以統稱他們為名字(Name)。其中較為常見的就是一些分布式服務框架中的服務地址列表。通過調用ZK提供的創建節點的API,能夠很容易創建一個全局唯一的path,這個path就可以作為一個名稱。
2、配置管理
程序總是需要配置的,如果程序分散部署在多台機器上,要逐個改變配置就變得困難。現在把這些配置全部放到zookeeper上去,保存在 Zookeeper 的某個目錄節點中,然后所有相關應用程序對這個目錄節點進行監聽,一旦配置信息發生變化,每個應用程序就會收到 Zookeeper 的通知,然后從 Zookeeper 獲取新的配置信息應用到系統中就好。
3、集群管理
所謂集群管理無在乎兩點:是否有機器退出和加入、選舉master。
對於第一點,所有機器約定在父目錄GroupMembers下創建臨時目錄節點,然后監聽父目錄節點的子節點變化消息。一旦有機器掛掉,該機器與zookeeper的連接斷開,其所創建的臨時目錄節點被刪除,所有其他機器都收到通知:某個兄弟目錄被刪除,於是,所有人都知道:它上船了。新機器加入也是類似,所有機器收到通知:新兄弟目錄加入,highcount又有了。
對於第二點,我們稍微改變一下,所有機器創建臨時順序編號目錄節點,每次選取編號最小的機器作為master就好。
4、分布式鎖
有了zookeeper的一致性文件系統,鎖的問題變得容易。鎖服務可以分為兩類,一個是保持獨占,另一個是控制時序。
對於第一類,我們將zookeeper上的一個znode看作是一把鎖,通過createznode的方式來實現。所有客戶端都去創建 /distribute_lock 節點,最終成功創建的那個客戶端也即擁有了這把鎖。廁所有言:來也沖沖,去也沖沖,用完刪除掉自己創建的distribute_lock 節點就釋放出鎖。
對於第二類, /distribute_lock 已經預先存在,所有客戶端在它下面創建臨時順序編號目錄節點,和選master一樣,編號最小的獲得鎖,用完刪除,依次方便。
5、隊列管理
兩種類型的隊列:
1、同步隊列,當一個隊列的成員都聚齊時,這個隊列才可用,否則一直等待所有成員到達。
2、隊列按照 FIFO 方式進行入隊和出隊操作。
第一類,在約定目錄下創建臨時目錄節點,監聽節點數目是否是我們要求的數目。
第二類,和分布式鎖服務中的控制時序場景基本原理一致,入列有編號,出列按編號。
6、負載均衡
這里說的負載均衡是指軟負載均衡。在分布式環境中,為了保證高可用性,通常同一個應用或同一個服務的提供方都會部署多份,達到對等服務。而消費者就須要在這些對等的服務器中選擇一個來執行相關的業務邏輯,其中比較典型的是消息中間件中的生產者,消費者負載均衡。
7、分布式通知/協調
ZooKeeper中特有watcher注冊與異步通知機制,能夠很好的實現分布式環境下不同系統之間的通知與協調,實現對數據變更的實時處理。使用方法通常是不同系統都對ZK上同一個znode進行注冊,監聽znode的變化(包括znode本身內容及子節點的),其中一個系統update了znode,那么另一個系統能夠收到通知,並作出相應處理。