本文中,咱們將對ZooKeeper進行介紹。簡單地說,ZooKeeper是一個用來在構成應用的各個子服務之間進行協調的一個服務。html
因爲其自己並無特別複雜的機制,所以咱們將會把更多的筆墨集中在如何對ZooKeeper進行使用方面。固然,這也是和其它博文所略有不一樣的地方,不然我也不會花費時間去寫這這篇博客。緩存
ZooKeeper簡介安全
咱們已經在前面一系列和集羣相關的博文中提到過,一個大型服務經常是由一系列子服務共同組成的。這些服務經常包含一系列動態的配置,以告知子服務當前程序運行環境的變化。那麼咱們須要怎樣完成配置的更新呢?這些配置的更改究竟是由誰來發起?若是有多個發起方同時對同一配置進行更改,那麼各個不一樣子服務接收到消息的前後順序將有所不一樣,進而致使各個子服務內部的動態配置互不相同。這一系列問題咱們應該如何解決?服務器
答案就是咱們要講解的ZooKeeper。ZooKeeper容許咱們將數據組織成爲一個相似於文件系統的數據結構。經過數據結點所在的地址,咱們能夠訪問數據結點中所包含的數據:網絡
爲了使用ZooKeeper,咱們的各個子服務須要經過一個客戶端與ZooKeeper鏈接,並經過一系列API調用執行對數據的讀寫操做。而爲了解決前面所提到的一系列和配置管理相關的問題,ZooKeeper提供了以下一系列特性:數據結構
下面就讓咱們依次對這些特性進行講解。多線程
首先就是wait free特性。讓咱們想象一種狀況,那就是對多條配置項的同時改寫。若是每次調用都須要等待全部更改完成,那麼整個更新的過程將變得很是緩慢:異步
從上圖中能夠看到,一個非阻塞調用能夠直接返回,從而能夠消除由於等待響應所致使的阻塞,顯著地提升子服務的運行效率。這種效率的提升在同時更改多個配置時很是顯著。由此ZooKeeper所提供的API主要分爲阻塞和非阻塞兩種。函數
接下來就是各個請求的FIFO執行以及線性讀寫。其實若是您作過多線程,這種保證很是容易理解。假設如今執行了兩個寫入請求,那麼寫入的前後順序不一樣可能致使運行結果不一樣:性能
而這種前後順序變化頗有多是因爲網絡延遲所致使的。試想一下,若是咱們在非阻塞模式中使用了同一客戶端插入了對同一個配置的兩個寫入操做,那麼咱們就須要保證後一個寫入操做是有效的,並且再次讀取必定能讀取後一個寫入操做所寫入的值。在version的幫助下,咱們能夠避免計劃外寫入,而單一客戶端的請求會按照FIFO順序執行的則能夠用來保證寫入請求後的讀取會讀取最後寫入的值。這也是ZooKeeper可以提供異步模式的基礎。
而爲了提升讀寫速度,ZooKeeper會在客戶端保存數據的緩存。這樣就可使得對數據的重複使用再也不須要從ZooKeeper服務中重複讀取。而爲了保證客戶端緩存和ZooKeeper服務記錄的數據一致,ZooKeeper使用了了一種被稱爲watch的機制:若是一個客戶端發送一個請求,並在該請求中設置了watch標記,那麼該客戶端就會在該數據上創建watch機制。一旦該結點所記錄的數據發生了變化,那麼它將向客戶端發送一個消息以通知該數據已經失效。該通知只聲明相應的數據發生了更改 ,卻不會指出到底更改成哪一個值。若是客戶端須要讀取更新的值,那麼它就須要從新執行一次讀取,並在該請求中決定究竟是否再次經過watch標記繼續偵聽。也就是說,watch機制是一種一次性的機制。其運行機制大體以下圖所示:
而另外一個使watch機制失效的即是會話結束。這抑或是子服務自動退出服務等正常業務邏輯執行(例如在使用Amazon的Spot Instance時會經常遇到這種狀況),或者是子服務失效等非正常狀況。
除此以外,ZooKeeper還有一個比較重要的概念就是數據的版本。相信使用過樂觀鎖等機制的讀者都應該知道,版本實際上就是爲了防止過時數據的。並且咱們也在剛剛的講解中提到過版本的做用,所以在這裏不作詳述。
因爲ZooKeeper經常管理着整個集羣中全部的動態配置信息,所以它自身已經包含了高可用性設計。一般狀況下,ZooKeeper的高可用性經常是經過多個服務實例來完成的。不一樣的客戶端可能鏈接到不一樣的服務實例上:
這些服務實例中,一個服務器將扮演leader角色,而其它服務實例將扮演follower角色。在一個讀請求到達時,任何服務實例將能夠直接將其所記錄的數據返回。而在須要寫入數據的時候,接收到寫入請求的服務實例會將該請求轉向ZooKeeper的leader,而後再由follower來對數據進行更新:
而爲了能從失效狀態恢復,ZooKeeper會按期保存數據的快照,並記錄對數據的操做日誌。若是一個ZooKeeper實例失效,那麼咱們就能夠經過選取最近的Snapshot並從新執行相應的操做日誌來完成該實例的恢復。
這裏有一個問題,那就是leader是如何決定的,以及在leader失效時如何獲得新的leader。這一切都是經過其內置的選舉協議來完成的。一種說法是,leader的選舉是按照一種特殊的Quorum選舉方式,Simple Majority Quorums來完成的。簡單地說,其表示絕大多數結點贊成的選舉方式。而另外一種說法則是按照Paxos協議族來完成的。無論怎樣,您只要知曉ZooKeeper中有一個leader便可。
最後要說的一點就是數據結點的類型。在ZooKeeper中,數據結點主要分爲兩種類型:Regular以及Ephemeral。Regular結點須要經過客戶端顯式地建立及刪除,而Ephemeral結點則會在會話結束之時即被銷燬。
OK。如今呢,基本上全部的基礎機制都已經介紹完畢了。那麼在本節最後,就讓咱們來看看ZooKeeper所提供的一系列API。在這裏,咱們並不區分API是同步的仍是異步的:
ZooKeeper使用示例
在瞭解了ZooKeeper的使用以後,咱們如今就以一個簡單的示例來看看到底如何使用ZooKeeper。
在《On cloud, be cloud native》一文中,咱們穿插着介紹了Amazon所提供的一系列服務。其中兩個重要的服務就是AMI和Auto Scaling Group。二者組合能夠制定出高效的可擴展(Scalability,我更傾向「伸縮」一詞),高可用集羣。在啓動時,Auto Scaling Group會使用指定的AMI建立虛擬機實例並執行動態腳本對建立好的虛擬機進行配置。這其中就包括從ZooKeeper中讀取做爲動態腳本輸入的動態配置信息:
從上圖中能夠看到,在程序啓動時,其將直接從ZooKeeper中直接讀取已經放置在特定位置的配置文件。該配置文件中記錄了虛擬機實例所須要運行的代碼在S3上的地址。接下來,虛擬機實例將會從Amazon S3中下載並部署這些代碼,並動態地對虛擬機進行配置。而在該讀取配置的過程當中,新建立的虛擬機實例將會把watch選項設置爲true。
而在程序運行一段時間後,咱們可能就須要對該Auto Scaling Group所承載的服務進行升級。此時咱們首先須要將更新後的Source Bundle放置在Amazon S3上,並更新ZooKeeper中所記錄的配置。因爲以前在建立虛擬機時咱們已經將watch選項設置爲true,所以該配置更改會發送一條通知到Auto Scaling Group中的各個虛擬機實例之上。接下來,這些虛擬機經過向Auto Scaling Group發送Detach消息從該Auto Scaling Group移除。在該Detach消息中,咱們應選擇保持Auto Scaling Group容量不變這一選項。這樣作的結果就是,Auto Scaling Group將會使用新的配置從新建立一臺新的虛擬機。這樣咱們只須要對配置進行更改,而其它的一切工做都由Amazon的各類自動化功能自行完成。
這個流程中有一個問題,那就是咱們要可以保證整個應用能持續地提供服務(假設更新後的服務邏輯具備後向兼容性)。當配置更改消息發送到各個虛擬機時,它們可能都會開始嘗試從Auto Scaling Group移除,從而致使Auto Scaling Group的容量快速降低,進而致使整個應用沒法提供服務。而解決方案即是經過ZooKeeper實現互斥鎖的功能,從而使得Auto Scaling Group中的各個實例的移除是逐個進行的。
基於ZooKeeper的互斥鎖的實現很是簡單:建立一個「lock file」便可。這種方法實際上在不少軟件中被使用。例如最多見的就是Git。其原理很簡單,那就是嘗試建立一個文件。若是文件建立成功,那麼其就是獲得了鎖。而若是因爲文件已經存在而建立失敗,那麼就是沒有獲得鎖。而在基於ZooKeeper的互斥鎖實現中,咱們嘗試建立的則是一個結點:爲了得到鎖,ZooKeeper的客戶端會嘗試在特定路徑下建立一個Ephemeral結點。若是其建立成功,那麼它就能夠獲得該鎖。而其它各個沒有成功得到鎖的實例將經過發送一個讀請求並標示watch標誌來偵聽該結點的變化。一旦擁有鎖的實例發現Auto Scaling Group已經建立好了新的虛擬機,那麼它就能夠向ZooKeeper發送請求刪除該文件。此時其它等待的實例將獲得通知並再次嘗試得到鎖。
那麼擁有鎖的實例是如何得知新的虛擬機已經建立完畢了呢?其使用的就是ZooKeeper的另一種常見使用方法:Group Membership。在ZooKeeper中,咱們能夠建立一個結點,並在它之下添加/刪除子結點。而在父結點進行監聽的客戶端將因爲子結點的增刪獲得通知消息。在獲得通知以後,其就能夠經過getChildren()函數獲得全部的子結點。也就是說,在獲得鎖並將自身移出Auto Scaling Group的虛擬機實例能夠首先經過getChildren()函數添加對Auto Scaling Group所對應結點的監聽。在新的虛擬機實例建立完畢並添加到Auto Scaling Group中後,其將向Auto Scaling Group所對應的結點添加一個子結點,從而通知本來移出Auto Scaling Group的虛擬機實例新的虛擬機實例已經建立完畢。那麼原虛擬機實例就能夠釋放互斥鎖,從而使得下一個虛擬機開始執行替換的步驟。
經過該互斥鎖實現,咱們可以讓Auto Scaling Group中的各虛擬機實例逐個移除並替換。固然啊,網絡上還有不少種對ZooKeeper的精妙使用。這裏就再也不一一贅述了。
轉載請註明原文地址並標明轉載:http://www.cnblogs.com/loveis715/p/5185796.html
商業轉載請事先與我聯繫:silverfox715@sina.com
公衆號必定幫忙別標成原創,由於協調起來太麻煩了。。。