1. 程式人生 > >ZooKeeper學習第八期——ZooKeeper伸縮性

ZooKeeper學習第八期——ZooKeeper伸縮性

一、ZooKeeper中Observer

1.1 ZooKeeper角色

經過前面的介紹,我想大家都已經知道了在ZooKeeper叢集當中有兩種角色Leader和Follower。Leader可以接受client 請求,也接收其他Server轉發的寫請求,負責更新系統狀態。 Follower也可以接收client請求,如果是寫請求將轉發給Leader來更新系統狀態,讀請求則由Follower的記憶體資料庫直接響應。 ZooKeeper叢集如圖1.1所示。

圖 1.1 ZooKeeper叢集服務

但在ZooKeeper的3.3.3版本以後,ZooKeeper中又添加了一種新角色Observer。Observer的作用同Follower類似,唯一區別就是它不參與選主過程。那麼,我們就可以根據該特性將ZK叢集中的Server分為兩種:

(1) 投票Server:Leader、Follower

(2) 非投票Server:Observer

1.2 為什麼引入Observer

(1) ZooKeeper可伸縮性

那麼,ZooKeeper為什麼要引入Observer這個角色呢?其實在ZooKeeper中引入Observer,主要是為了使 ZooKeeper具有更好的可伸縮性。那麼,何為可伸縮性?關於伸縮性,對於不同的人意味著不同的事情。 而在這裡是說,如果我們的工作負載可以通過給系統分配更多的資源來分擔,那麼這個系統就是可伸縮的;一個不可伸縮的系統卻無法通過增加資源來提升效能,甚 至會在工作負載增加時,效能會急劇下降。

在Observer出現以前,ZooKeeper的伸縮性由Follower來實現,我們可以通過新增Follower節點的數量來保證 ZooKeeper服務的讀效能。但是隨著Follower節點數量的增加,ZooKeeper服務的寫效能受到了影響。為什麼會出現這種情況?在此,我 們需要首先了解一下這個"ZK服務"是如何工作的。

(2) ZK服務過程

ZooKeeper服務中的每個Server可服務於多個Client,並且Client可連線到ZK服務中的任一臺Server來提交請求。若是讀請求,則由每臺Server的本地副本資料庫直接響應。若是改變Server狀態的寫請求,需要通過一致性協議來處理,這個協議就是我們前面介紹的Zab協議。

簡單來說,Zab協議規定:來自Client的所有寫請求,都要轉發給ZK服務中唯一的ServerLeader, 由Leader根據該請求發起一個Proposal。然後,其他的Server對該Proposal進行Vote。之後,Leader對Vote進行收 集,當Vote數量過半時Leader會向所有的Server傳送一個通知訊息。最後,當Client所連線的Server收到該訊息時,會把該操作更新 到記憶體中並對Client的寫請求做出迴應。該工作流程如下圖1.2所示。

圖1.2 ZK 寫請求工作流程圖

從圖中我們可以看出, ZooKeeper 伺服器在上述協議中實際扮演了兩個職能。它們一方面從客戶端接受連線與操作請求,另一方面對操作結果進行投票。這兩個職能在 ZooKeeper叢集擴充套件的時候彼此制約。例如,當我們希望增加 ZK服務中Client數量的時候,那麼我們就需要增加Server的數量,來支援這麼多的客戶端。然而,從Zab協議對寫請求的處理過程中我們可以發 現,增加伺服器的數量,則增加了對協議中投票過程的壓力。因為Leader節點必須等待叢集中過半Server響應投票,於是節點的增加使得部分計算機運 行較慢,從而拖慢整個投票過程的可能性也隨之提高,寫操作也會隨之下降。這正是我們在實際操作中看到的問題——隨著 ZooKeeper 叢集變大,寫操作的吞吐量會下降。

(3) ZooKeeper擴充套件

所以,我們不得不,在增加Client數量的期望和我們希望保持較好吞吐效能的期望間進行權衡。要打破這一耦合關係,我們引入了不參與投票的服務 器,稱為 Observer。 Observer可以接受客戶端的連線,並將寫請求轉發給Leader節點。但是,Leader節點不會要求 Observer參加投票。相反,Observer不參與投票過程,僅僅在上述第3歩那樣,和其他服務節點一起得到投票結果。

圖 1.3 Observer 寫吞吐量測試

圖1.3 顯示了一個簡單評測的結果。縱軸是,單一客戶端能夠發出的每秒鐘同步寫操作的數量。橫軸是 ZooKeeper 叢集的尺寸。藍色的是每個伺服器都是投票Server的情況,而綠色的則只有三個是投票Server,其它都是 Observer。從圖中我們可以看出,我們在擴充 Observer時寫效能幾乎可以保持不便。但是,如果擴充套件投票Server的數量,寫效能會明顯下降,顯然 Observers 是有效的。

這個簡單的擴充套件,給 ZooKeeper 的可伸縮性帶來了全新的映象。我們現在可以加入很多 Observer 節點,而無須擔心嚴重影響寫吞吐量。但他並非是無懈可擊的,因為協議中的通知階段,仍然與伺服器的數量呈線性關係。但是,這裡的序列開銷非常低。因此,我 們可以認為在通知伺服器階段的開銷無法成為主要瓶頸。

二、Observer應用

(1) Observer提升讀效能的可伸縮性

應對Client的數量增加,是 Observer的一個重要用例,但是實際上它還給叢集帶來很多其它的好處。Observer作為ZooKeeper的一個優化,Observer伺服器可以直接獲取Leader的本地資料儲存,而無需經過投票過程。但這也面臨一定的"時光旅行"風險,也就是說:可能在讀到新值之後又讀到老值。但這隻在伺服器故障時才會發生事實上,在這種情況下,Client可以通過"sync"操作來保證下一個值是最新的。

因此,在大量讀操作的工作負載下,Observer會使ZooKeeper的效能得到巨大提升。若要增加投票Server數量來承擔讀操作,那麼就 會影響ZooKeeper服務的寫效能。而且Observer允許我們將讀效能和寫效能分開,這使ZooKeeper更適用於一些以讀為主的應用場景。

(2) Observer提供了廣域網能力

Observer還能做更多。Observer對於跨廣域網連線的Client來說是很好的候選方案。Observer可作為候選方案,原因有三:

① 為了獲得很好的讀效能,有必要讓客戶端離伺服器儘量近,這樣往返時延不會太高。然而,將 ZooKeeper 叢集分散到兩個叢集是非常不可取的設計,因為良好配置的 ZooKeeper 應該讓投票伺服器間用低時延連線互連——否則,我們將會遇到上面提到的低反映速度的問題。

② 而Observer 可以被部署在,需要訪問 ZooKeeper 的任意資料中心中。這樣,投票協議不會受到資料中心間鏈路的高時延的影響,效能得到提升。投票過程中 Observer 和領導節點間的訊息遠少於投票伺服器和領導節點間的訊息。這有助於在遠端資料中心高寫負載的情況下降低頻寬需求。

③ 由於Observer即使失效也不會影響到投票叢集,這樣如果資料中心間鏈路發生故障,不會影響到服務本身的可用性。這種故障的發生概率要遠高於一個數據中心中機架間的連線的故障概率,所以不依賴於這種鏈路是個優點。

三、ZooKeeper叢集搭建案例

前面介紹了ZooKeeper叢集中的幾種角色,接下來給大家來介紹一下如何利用這些角色,來搭建一個性能良好的ZooKeeper叢集。我以一個專案為例,給大家分析一下該如何規劃我們的ZooKeeper叢集。

假設我們的專案需要進行跨機房操作,我們的總部機房設在杭州,但他還要同美國,青島等多個機房之間進行資料互動。但機房之間的網路延遲都比較大,比如中美機房走海底光纜有ping操作200ms的延遲,杭州和青島機房有70ms的延遲。 為了提升系統的網路效能,我們在部署ZooKeeper網路時會在每個機房部署節點,多個機房之間再組成一個大的網路,來保證整個ZK叢集資料一致性。

根據前面的介紹,最後的部署結構就會是:

(總部) 杭州機房  >=3臺 :由Leader/Follower構成的投票叢集

(分支) 青島機房  >=1臺 :由Observer構成的ZK叢集

(分支) 美國機房  >=1臺  : 由Observer構成的ZK叢集

圖 3.1 ZooKeeper叢集部署圖

從圖中我們可以看出,我們在單個機房內組成一個投票叢集,外圍的機房都會是一個Observer叢集和投票叢集進行資料互動。 至於這樣部署的一些好處,大家自己根據我前面對ZooKeeper角色的介紹,對比著體會一下,我想這樣更能幫助大家理解ZooKeeper。而且針對這 樣的部署結構,我們會引入一個優先叢集問題: 比如在美國機房的Client,需要優先去訪問本機房的ZK叢集,訪問不到才去訪問HZ(總部)機房。