爲何不該該使用Zookeeper作服務發現?(轉載)
【編者的話】本文做者經過ZooKeeper與Eureka做爲
Service發現服務
(注:WebServices體系中的UDDI就是個發現服務)的優劣對比,分享了Knewton在雲計算平臺部署服務的經驗。本文雖然略顯偏激,可是看得出Knewton在雲平臺方面是很是有經驗的,這篇文章從實踐角度出發分別從雲平臺特色、CAP原理以及運維三個方面對比了ZooKeeper與Eureka兩個系統做爲發佈服務的優劣,並提出了在雲平臺構建發現服務的方法論。
背景不少公司選擇使用
ZooKeeper
做爲Service發現服務(Service Discovery),可是在構建
Knewton
(Knewton是一個提供個性化教育平臺的公司、學校和出版商能夠經過Knewton平臺爲學生提供自適應的學習材料)平臺時,咱們發現這是個根本性的錯誤。在這邊文章中,咱們將用咱們在實踐中遇到的問題來講明,爲何使用ZooKeeper作Service發現服務是個錯誤。
請留意服務部署環境讓咱們從頭開始梳理。咱們在部署服務的時候,應該首先考慮服務部署的平臺(平臺環境),而後才能考慮平臺上跑的軟件系統或者如何在選定的平臺上本身構建一套系統。例如,對於雲部署平臺來講,平臺在硬件層面的伸縮(注:做者應該指的是系統的冗餘性設計,即系統遇到單點失效問題,可以快速切換到其餘節點完成任務)與如何應對網絡故障是首先要考慮的。當你的服務運行在大量服務器構建的集羣之上時(注:原話爲大量可替換設備),則確定會出現單點故障的問題。對於knewton來講,咱們雖然是部署在AWS上的,可是在過往的運維中,咱們也遇到過形形色色的故障;因此,你應該把系統設計成「故障開放型」(expecting failure)的。其實有不少一樣使用AWS的
公司
跟咱們遇到了(同時有不少
書
是介紹這方面的)類似的問題。你必須可以提早預料到平臺可能會出現的問題如:意外故障(注:原文爲box failure,只能意會到做者指的是意外彈出的錯誤提示框),高延遲與
網絡分割問題
(注:原文爲network partitions。意思是當網絡交換機出故障會致使不一樣子網間通信中斷)——同時咱們要能構建足夠彈性的系統來應對它們的發生。
永遠不要指望你部署服務的平臺跟其餘人是同樣的!固然,若是你在獨自運維一個數據中心,你可能會花不少時間與錢來避免硬件故障與網絡分割問題,這是另外一種狀況了;可是在雲計算平臺中,如AWS,會產生不一樣的問題以及不一樣的解決方式。當你實際使用時你就會明白,可是,你最好提早應對它們(注:指的是上一節說的意外故障、高延遲與網絡分割問題)的發生。
ZooKeeper做爲發現服務的問題ZooKeeper(注:ZooKeeper是著名Hadoop的一個子項目,旨在解決大規模分佈式應用場景下,服務協調同步(Coordinate Service)的問題;它能夠爲同在一個分佈式系統中的其餘服務提供:統一命名服務、配置管理、分佈式鎖服務、集羣管理等功能)是個偉大的開源項目,它很成熟,有至關大的社區來支持它的發展,並且在生產環境獲得了普遍的使用;可是用它來作Service發現服務解決方案則是個錯誤。
在分佈式系統領域有個著名的
CAP定理
(C-數據一致性;A-服務可用性;P-服務對網絡分區故障的容錯性,這三個特性在任何分佈式系統中不能同時知足,最多同時知足兩個);ZooKeeper是個CP的,即任什麼時候刻對ZooKeeper的訪問請求能獲得一致的數據結果,同時系統對網絡分割具有容錯性;可是它不能保證每次服務請求的可用性(注:也就是在極端環境下,ZooKeeper可能會丟棄一些請求,消費者程序須要從新請求才能得到結果)。可是別忘了,ZooKeeper是分佈式協調服務,它的職責是保證數據(注:配置數據,狀態數據)在其管轄下的全部服務之間保持同步、一致;因此就不難理解爲何ZooKeeper被設計成CP而不是AP特性的了,若是是AP的,那麼將會帶來恐怖的後果(注:ZooKeeper就像交叉路口的信號燈同樣,你能想象在交通要道忽然信號燈失靈的狀況嗎?)。並且,做爲ZooKeeper的核心實現算法
Zab
,就是解決了分佈式系統下數據如何在多個服務之間保持同步問題的。
做爲一個分佈式協同服務,ZooKeeper很是好,可是對於Service發現服務來講就不合適了;由於對於Service發現服務來講就算是返回了包含不實的信息的結果也比什麼都不返回要好;再者,對於Service發現服務而言,寧肯返回某服務5分鐘以前在哪幾個服務器上可用的信息,也不能由於暫時的網絡故障而找不到可用的服務器,而不返回任何結果。因此說,用ZooKeeper來作Service發現服務是確定錯誤的,若是你這麼用就慘了!
並且更況且,若是被用做Service發現服務,ZooKeeper自己並無正確的處理網絡分割的問題;而在雲端,網絡分割問題跟其餘類型的故障同樣的確會發生;因此最好提早對這個問題作好100%的準備。就像
Jepsen
在ZooKeeper網站上發佈的博客中所說:在ZooKeeper中,若是在同一個網絡分區(partition)的節點數(nodes)數達不到ZooKeeper選取Leader節點的「法定人數」時,它們就會從ZooKeeper中斷開,固然同時也就不能提供Service發現服務了。
若是給ZooKeeper加上客戶端緩存(注:給ZooKeeper節點配上本地緩存)或者其餘相似技術的話能夠緩解ZooKeeper由於網絡故障形成節點同步信息錯誤的問題。
Pinterest
與
Airbnb
公司就使用了這個方法來防止ZooKeeper故障發生。這種方式能夠從表面上解決這個問題,具體地說,當部分或者全部節點跟ZooKeeper斷開的狀況下,每一個節點還能夠從本地緩存中獲取到數據;可是,即使如此,ZooKeeper下全部節點不可能保證任什麼時候候都能緩存全部的服務註冊信息。若是ZooKeeper下全部節點都斷開了,或者集羣中出現了網絡分割的故障(注:因爲交換機故障致使交換機底下的子網間不能互訪);那麼ZooKeeper會將它們都從本身管理範圍中剔除出去,外界就不能訪問到這些節點了,即使這些節點自己是「健康」的,能夠正常提供服務的;因此致使到達這些節點的服務請求被丟失了。(注:這也是爲何ZooKeeper不知足CAP中A的緣由)
更深層次的緣由是,ZooKeeper是按照CP原則構建的,也就是說它能保證每一個節點的數據保持一致,而爲ZooKeeper加上緩存的作法的目的是爲了讓ZooKeeper變得更加可靠(available);可是,ZooKeeper設計的本意是保持節點的數據一致,也就是CP。因此,這樣一來,你可能既得不到一個數據一致的(CP)也得不到一個高可用的(AP)的Service發現服務了;由於,這至關於你在一個已有的CP系統上強制栓了一個AP的系統,這在本質上就行不通的!一個Service發現服務應該從一開始就被設計成高可用的才行!
若是拋開CAP原理無論,正確的設置與維護ZooKeeper服務就很是的困難;錯誤會
常常發生
,致使不少工程被創建只是爲了減輕維護ZooKeeper的難度。這些錯誤不只存在與客戶端並且還存在於ZooKeeper服務器自己。Knewton平臺不少故障就是因爲ZooKeeper使用不當而致使的。那些看似簡單的操做,如:正確的重建觀察者(reestablishing watcher)、客戶端Session與異常的處理與在ZK窗口中管理內存都是很是容易致使ZooKeeper出錯的。同時,咱們確實也遇到過ZooKeeper的一些經典bug:
ZooKeeper-1159
與
ZooKeeper-1576
;咱們甚至在生產環境中遇到過ZooKeeper選舉Leader節點失敗的狀況。這些問題之因此會出現,在於ZooKeeper須要管理與保障所管轄服務羣的Session與網絡鏈接資源(注:這些資源的管理在分佈式系統環境下是極其困難的);可是它不負責管理服務的發現,因此使用ZooKeeper當Service發現服務得不償失。
作出正確的選擇:Eureka的成功咱們把Service發現服務從ZooKeeper切換到了Eureka平臺,它是一個開源的服務發現解決方案,由Netflix公司開發。(注:Eureka由兩個組件組成:Eureka服務器和Eureka客戶端。Eureka服務器用做服務註冊服務器。Eureka客戶端是一個java客戶端,用來簡化與服務器的交互、做爲輪詢負載均衡器,並提供服務的故障切換支持。)Eureka一開始就被設計成高可用與可伸縮的Service發現服務,這兩個特色也是Netflix公司開發全部平臺的兩個特點。(
他們都在討論Eureka
)。自從切換工做開始到如今,咱們實現了在生產環境中全部依賴於Eureka的產品沒有下線維護的記錄。咱們也被告知過,在雲平臺作服務遷移註定要遇到失敗;可是咱們從這個例子中獲得的經驗是,一個優秀的Service發現服務在其中發揮了相當重要的做用!
首先,在Eureka平臺中,若是某臺服務器宕機,Eureka不會有相似於ZooKeeper的選舉leader的過程;客戶端請求會自動切換到新的Eureka節點;當宕機的服務器從新恢復後,Eureka會再次將其歸入到服務器集羣管理之中;而對於它來講,全部要作的無非是同步一些新的服務註冊信息而已。因此,不再用擔憂有「掉隊」的服務器恢復之後,會從Eureka服務器集羣中剔除出去的風險了。Eureka甚至被設計用來應付範圍更廣的網絡分割故障,並實現「0」宕機維護需求。當網絡分割故障發生時,每一個Eureka節點,會持續的對外提供服務(注:ZooKeeper不會):接收新的服務註冊同時將它們提供給下游的服務發現請求。這樣一來,就能夠實如今同一個子網中(same side of partition),新發布的服務仍然能夠被發現與訪問。
可是,Eureka作到的不止這些。正常配置下,Eureka內置了心跳服務,用於淘汰一些「瀕死」的服務器;若是在Eureka中註冊的服務,它的「心跳」變得遲緩時,Eureka會將其整個剔除出管理範圍(這點有點像ZooKeeper的作法)。這是個很好的功能,可是當網絡分割故障發生時,這也是很是危險的;由於,那些由於網絡問題(注:心跳慢被剔除了)而被剔除出去的服務器自己是很」健康「的,只是由於網絡分割故障把Eureka集羣分割成了獨立的子網而不能互訪而已。
幸運的是,Netflix考慮到了這個缺陷。若是Eureka服務節點在短期裏丟失了大量的心跳鏈接(注:可能發生了網絡故障),那麼這個Eureka節點會進入」自我保護模式「,同時保留那些「心跳死亡「的服務註冊信息不過時。此時,這個Eureka節點對於新的服務還能提供註冊服務,對於」死亡「的仍然保留,以防還有客戶端向其發起請求。當網絡故障恢復後,這個Eureka節點會退出」自我保護模式「。因此Eureka的哲學是,同時保留」好數據「與」壞數據「總比丟掉任何」好數據「要更好,因此這種模式在實踐中很是有效。
最後,Eureka還有客戶端緩存功能(注:Eureka分爲客戶端程序與服務器端程序兩個部分,客戶端程序負責向外提供註冊與發現服務接口)。因此即使Eureka集羣中全部節點都失效,或者發生網絡分割故障致使客戶端不能訪問任何一臺Eureka服務器;Eureka服務的消費者仍然能夠經過Eureka客戶端緩存來獲取現有的服務註冊信息。甚至最極端的環境下,全部正常的Eureka節點都不對請求產生相應,也沒有更好的服務器解決方案來解決這種問題時;得益於Eureka的客戶端緩存技術,消費者服務仍然能夠經過Eureka客戶端查詢與獲取註冊服務信息,這點很重要。
Eureka的構架保證了它可以成爲Service發現服務。它相對與ZooKeeper來講剔除了Leader節點的選取或者事務日誌機制,這樣作有利於減小使用者維護的難度也保證了Eureka的在運行時的健壯性。並且Eureka就是爲發現服務所設計的,它有獨立的客戶端程序庫,同時提供心跳服務、服務健康監測、自動發佈服務與自動刷新緩存的功能。可是,若是使用ZooKeeper你必須本身來實現這些功能。Eureka的全部庫都是開源的,全部人都能看到與使用這些源代碼,這比那些只有一兩我的能看或者維護的客戶端庫要好。
維護Eureka服務器也很是的簡單,好比,切換一個節點只須要在現有EIP下移除一個現有的節點而後添加一個新的就行。Eureka提供了一個web-based的圖形化的運維界面,在這個界面中能夠查看Eureka所管理的註冊服務的運行狀態信息:是否健康,運行日誌等。Eureka甚至提供了Restful-API接口,方便第三方程序集成Eureka的功能。
結論關於Service發現服務經過本文咱們想說明兩點:一、留意服務運行的硬件平臺;二、時刻關注你要解決的問題,而後決定使用什麼平臺。Knewton就是從這兩個方面考慮使用Eureka替換ZooKeeper來做爲service發現服務的。雲部署平臺是充滿不可靠性的,Eureka能夠應對這些缺陷;同時Service發現服務必須同時具有高可靠性與高彈性,Eureke就是咱們想要的!
歡迎關注本站公眾號,獲取更多信息