ZooKeeper入門教程一簡介與核心概念
本章是后續(xù)學(xué)習(xí)的基石,只有充分理解了分布式系統(tǒng)的概念和面臨的問題,以及ZooKeeper內(nèi)部的概念,才能懂得ZooKeeper是如何對分布式系統(tǒng)進行協(xié)調(diào),為后續(xù)學(xué)習(xí)打下堅實的基礎(chǔ)。
1、ZooKeeper介紹與核心概念
1.1 簡介
ZooKeeper最為主要的使用場景,是作為分布式系統(tǒng)的分布式協(xié)同服務(wù)。在學(xué)習(xí)zookeeper之前,先要對分布式系統(tǒng)的概念有所了解,否則你將完全不知道zookeeper在分布式系統(tǒng)中起到了什么作用,解決了什么問題。
1.2分布式系統(tǒng)面臨的問題
我們將分布式系統(tǒng)定義為:分布式系統(tǒng)是同時跨越多個物理主機,獨立運行的多個軟件所組成系統(tǒng)。類比一下,分布式系統(tǒng)就是一群人一起干活。人多力量大,每個服務(wù)器的算力是有限的,但是通過分布式系統(tǒng),由n個服務(wù)器組成起來的集群,算力是可以無限擴張的。
優(yōu)點顯而易見,人多干活快,并且互為備份。但是缺點也很明顯。我們可以想象一下,以一個小研發(fā)團隊開發(fā)軟件為例,假設(shè)我們有一個5人的項目組,要開始一個系統(tǒng)的開發(fā),項目組將面臨如下問題:
你一定在想,以上這些問題很簡單啊,在我的日常工作中天天都在發(fā)生,并沒感覺有什么復(fù)雜。是的,這是因為我們?nèi)祟惖拇竽X是個超級計算機,能夠靈活應(yīng)對這些問題,而且現(xiàn)實中信息的交換不依賴網(wǎng)絡(luò),不會因網(wǎng)絡(luò)延遲或者中斷,出現(xiàn)信息不對等。而且現(xiàn)實中對以上問題的處理其實并不嚴謹,從而也引發(fā)了很多問題。
想一想,項目中是不是出現(xiàn)過溝通不暢造成任務(wù)分配有歧義?是否由于人員離職造成任務(wù)進行不下去,甚至要聯(lián)系離職人員協(xié)助?是不是出現(xiàn)過任務(wù)分配不合理?類似這樣的各種問題,肯定會發(fā)生于你的項目組中。
在現(xiàn)實世界,我們可以人為去協(xié)調(diào),即使出錯了,人工去補錯,加加班搞定就好。但在計算機的世界,這樣做是行不通的,一切都要保證嚴謹,以上問題要做到盡可能不要發(fā)生。因此,分布式系統(tǒng)必須采用合理的方式解決掉以上的問題。
實際上要想解決這些問題并沒有那么復(fù)雜,我們僅需要做一件事就可以萬事無憂---讓信息在項目組成員中同步。如果能做到信息同步,那么每個人在干什么,大家都是清楚的,干到什么程度也是清晰的,無論誰離職也不會產(chǎn)生問題。分配的工作,能夠及時清晰的同步給每個組員,確保每個組員收到的任務(wù)分配沒有沖突。
分布式系統(tǒng)的協(xié)調(diào)工作就是通過某種方式,讓每個節(jié)點的信息能夠同步和共享。這依賴于服務(wù)進程之間的通信。通信方式有兩種:
1、通過網(wǎng)絡(luò)進行信息共享
這就像現(xiàn)實世界,開發(fā)leader在會上把任務(wù)傳達下去,組員通過聽leader命令或者看leader的郵件知道自己要干什么。當(dāng)任務(wù)分配有變化時,leader會單獨告訴組員,或者再次召開會議。信息通過人與人之間的直接溝通,完成傳遞。
2、通過共享存儲
這就好比開發(fā)leader按照約定的時間和路徑,把任務(wù)分配表放到了svn上,組員每天去svn上拉取最新的任務(wù)分配表,然后干活。其中svn就是共享存儲。更好一點的做法是,當(dāng)svn文件版本更新時,觸發(fā)郵件通知,每個組員再去拉取最新的任務(wù)分配表。這樣做更好,因為每次更新,組員都能第一時間得到消息,從而讓自己手中的任務(wù)分配表永遠是最新的。此種方式依賴于中央存儲。整個過程如下圖所示:
1.3 ZooKeeper如何解決分布式系統(tǒng)面臨的問題
ZooKeeper對分布式系統(tǒng)的協(xié)調(diào),使用的是第二種方式,共享存儲。其實共享存儲,分布式應(yīng)用也需要和存儲進行網(wǎng)絡(luò)通信。網(wǎng)絡(luò)通信是分布式系統(tǒng)并發(fā)設(shè)計的基礎(chǔ)。
實際上,通過ZooKeeper實現(xiàn)分布式協(xié)同的原理,和項目組通過SVN同步工作任務(wù)的例子是一樣的。ZooKeeper就像是svn,存儲了任務(wù)的分配、完成情況等共享信息。每個分布式應(yīng)用的節(jié)點就是組員,訂閱這些共享信息。當(dāng)主節(jié)點(組leader),對某個從節(jié)點的分工信息作出改變時,相關(guān)訂閱的從節(jié)點得到zookeeper的通知,取得自己最新的任務(wù)分配。完成工作后,把完成情況存儲到zookeeper。主節(jié)點訂閱了該任務(wù)的完成情況信息,所以將得到zookeeper的完工的通知。參考下圖,回味一下,是不是和前面項目組通過svn分配工作的例子一模一樣?僅僅是把svn和郵件系統(tǒng)合二為一,以ZooKeeper代替。
注:Slave節(jié)點要想獲取ZooKeeper的更新通知,需事先在關(guān)心的數(shù)據(jù)節(jié)點上設(shè)置觀察點。
大多數(shù)分布式系統(tǒng)中出現(xiàn)的問題,都源于信息的共享出了問題。如果各個節(jié)點間信息不能及時共享和同步,那么就會在協(xié)作過程中產(chǎn)生各種問題。ZooKeeper解決協(xié)同問題的關(guān)鍵,在于保證分布式系統(tǒng)信息的一致性。
通過以上章節(jié)的講解,我們應(yīng)該已經(jīng)理解分布式系統(tǒng)以及其面臨的問題。了解了ZooKeeper通過什么樣的機制去解決這些問題。從宏觀上對ZooKeeper已經(jīng)有了認知,接下來我們先切入到zookeeper自身,講解zookeeper的概念,這些概念很重要,所有zookeeper的應(yīng)用都會圍繞這些概念來實現(xiàn)。
1.4 zookeeper概念介紹
ZooKeeper并不直接暴露分布式服務(wù)所需要的原語及原語的調(diào)用方法。什么是原語?舉個例子,比如說分布式鎖機制是一個原語,它會暴露出創(chuàng)建、獲取、釋放三個調(diào)用方法。ZooKeeper以類似文件系統(tǒng)的方式存儲數(shù)據(jù),暴漏出調(diào)用這些數(shù)據(jù)的API。讓應(yīng)用通過ZooKeeper的機制和API,自己來實現(xiàn)分布式相關(guān)原語。
我們?nèi)粝胱寫?yīng)用能夠通過ZooKeeper實現(xiàn)分布式協(xié)同,那么第一件事就是了解ZooKeeper的特性及相關(guān)概念,另外熟悉它給我們提供了哪些API。
1.4.1 znode
Zookeeper會保存任務(wù)的分配、完成情況,等共享信息,那么ZooKeeper是如何保存的呢?在ZooKeeper中,這些信息被保存在一個個數(shù)據(jù)節(jié)點上,這些節(jié)點被稱為znode。它采用了類似文件系統(tǒng)的層級樹狀結(jié)構(gòu)進行管理。見下圖示例:
根節(jié)點/包含4個子節(jié)點,其中三個擁有下一級節(jié)點。有的葉子節(jié)點存儲了信息。
節(jié)點上沒有存儲數(shù)據(jù),也有著重要的含義。比如在主從模式中,當(dāng)/master節(jié)點沒有數(shù)據(jù)時,代表分布式應(yīng)用的主節(jié)點還沒有選舉出來。
znode節(jié)點存儲的數(shù)據(jù)為字節(jié)數(shù)組。存儲數(shù)據(jù)的格式zookeeper不做限制,也不提供解析,需要應(yīng)用自己實現(xiàn)。
實際上圖就是主從模式存儲數(shù)據(jù)的示例,這里先簡單講解:
/master
,存儲了當(dāng)前主節(jié)點的信息/workers
,下面的每個子znode代表一個從節(jié)點,子znode上存儲的數(shù)據(jù),如“foo.com:2181”,代表從節(jié)點的信息。/tasks
,下面的每個子znode代表一個任務(wù),子znode上存儲的信息如“run cmd”,代表該內(nèi)務(wù)內(nèi)容/assign
,下面每個子znode代表一個從節(jié)點的任務(wù)集合。如/assign/worker-1,代表worker-1這個從節(jié)點的任務(wù)集合。/assign/worker-1下的每個子znode代表分配給worker-1的一個任務(wù)。
持久節(jié)點(persistent)和臨時節(jié)點(ephemeral)
持久節(jié)點只能通過delete刪除。臨時節(jié)點在創(chuàng)建該節(jié)點的客戶端崩潰或關(guān)閉時,自動被刪除。
前面例子中的/master應(yīng)該使用臨時節(jié)點,這樣當(dāng)主節(jié)點失效或者退出時,該znode被刪除,其他節(jié)點知道主節(jié)點崩潰了,開始進行選舉的邏輯。另外/works/worker-1也應(yīng)該是臨時節(jié)點,在此從節(jié)點失效的時候,該臨時節(jié)點自動刪除。
在目前的版本,由于臨時znode會因為創(chuàng)建者會話過期被刪除,所以不允許臨時節(jié)點擁有子節(jié)點。
有序節(jié)點
znode可以被設(shè)置為有序(sequential)節(jié)點。有序znode節(jié)點被分配唯一一個單調(diào)遞增的證書。如果創(chuàng)建了個一有序節(jié)點為/workers/worker-,zookeeper會自動分配一個序號1,追加在名字后面,znode名稱為/workers/worker-1。通過這種方式,可以創(chuàng)建唯一名稱znode,并且可以直觀的看到創(chuàng)建的順序。
znode支持的操作及暴露的API:
create /path data
創(chuàng)建一個名為/path的znode,數(shù)據(jù)為data。
delete /path
刪除名為/path的znode。
exists /path
檢查是否存在名為/path的znode
setData /path data
設(shè)置名為/path的znode的數(shù)據(jù)為data
getData /path
返回名為/path的znode的數(shù)據(jù)
getChildren /path
返回所有/path節(jié)點的所有子節(jié)點列表
1.4.2 觀察與通知
分布式應(yīng)用需要及時知道zookeeper中znode的變化,從而了解到分布式應(yīng)用整體的狀況,如果采用輪詢方式,代價太大,絕大多數(shù)查詢都是無效的。因此,zookeeper采用了通知的機制??蛻舳讼騴ookeeper請求,在特定的znode設(shè)置觀察點(watch)。當(dāng)該znode發(fā)生變化時,會觸發(fā)zookeeper的通知,客戶端收到通知后進行業(yè)務(wù)處理。觀察點觸發(fā)后立即失效。所以一旦觀察點觸發(fā),需要再次設(shè)置新的觀察點。
我們使用Zookeeper不能期望能夠監(jiān)控到節(jié)點每次的變化。思考如下場景:
- 1、客戶端C1設(shè)置觀察點在/tasks
- 2、觀察點觸發(fā),C1處理自己的邏輯
- 3、C1設(shè)置新的觀察點前,C2更新了/tasks
- 4、C1處理完邏輯,再次設(shè)置了觀察點。
此時C1不會得到第三步的通知,因此錯過了C2更新/tasks這次操作。要想不錯過這次更新,C1需要在設(shè)置監(jiān)視點前讀取/tasks的數(shù)據(jù),進行對比,發(fā)現(xiàn)更新。
再如下面的場景:
- 1、客戶端C1設(shè)置觀察點在/tasks
- 2、/tasks上發(fā)生了連續(xù)兩次更新
- 3、C1在得到第一次更新的通知后就讀取了/tasks的數(shù)據(jù)
- 4、此時第二次更新也已經(jīng)發(fā)生,C1用第一次的通知,讀取到兩次更新后的數(shù)據(jù)
此時C1雖然錯過了第二次通知,但是C1最終還是讀取到了最新的數(shù)據(jù)。
因此Zookeeper只能保證最終的一致性,而無法保證強一致性。
zookeeper可以定義不同的觀察類型。例如觀察znode數(shù)據(jù)變化,觀察znode子節(jié)點變化,觀察znode創(chuàng)建或者刪除。
1.4.3 版本
每個znode都有版本號,隨著每次數(shù)據(jù)變化自增。setData和delete,以版本號作為參數(shù),當(dāng)傳入的版本號和服務(wù)器上不一致時,調(diào)用失敗。當(dāng)多個zookeeper客戶端同時對一個znode操作時,版本將會起到作用,假設(shè)c1,c2同時往一個znode寫數(shù)據(jù),c1先寫完后版本從1升為2,但是c2寫的時候攜帶版本號1,c2會寫入失敗。
1.4.4 法定人數(shù)
zookeeper服務(wù)器運行于兩種模式:獨立模式和仲裁模式(集群)。仲裁模式下,會復(fù)制所有服務(wù)器的數(shù)據(jù)樹。但如果讓客戶端等待所有復(fù)制完成,延遲太高。這里引入法定人數(shù)概念,指為了使zookeeper集群正常工作,必須有效運行的服務(wù)器數(shù)量。同時也是服務(wù)器通知客戶端保存成功前,必須保存數(shù)據(jù)的服務(wù)器最小數(shù)。例如我們有一個5臺服務(wù)器的zookeeper集群,法定人數(shù)為3,只要任何3個服務(wù)器保存了數(shù)據(jù),客戶端就會收到確認。只要有3臺服務(wù)器存活,整個zookeeper集群就是可用的。
下圖展示了客戶端提交請求到收到回復(fù)的過程:
法定人數(shù)需要大于服務(wù)器數(shù)量的一半。也稱為多數(shù)原則。舉個例子說明,假如集群有5臺服務(wù)器,法定人數(shù)為2,那么有2臺服務(wù)器參與復(fù)制即可,若這2臺server剛剛復(fù)制完/z這個znode,就掛掉了。此時剩下了3臺server,大于法定人數(shù)2,所以zookeeper認為集群正常,但這三臺服務(wù)器是無法發(fā)現(xiàn)/z這個znode的。如果法定人數(shù)大于服務(wù)器數(shù)量一半,那么法定人數(shù)復(fù)制完成,就可以確保集群存活時,至少有一臺服務(wù)器有最新的znode,否則集群認為自己已經(jīng)崩潰。
下面兩個例子闡明了,為何要遵循多數(shù)原則。
下圖展示了5臺server,法定人數(shù)為3,在確保zookeeper集群存活的前提下,最壞的情況掛了2臺server(剩余及器數(shù)量3>=法定人數(shù)3),zookeeper是如何能確保數(shù)據(jù)完備,集群繼續(xù)工作的。
接下來兩張圖展示了5臺server,未遵循多數(shù)原則,法定人數(shù)設(shè)為2。同樣掛了兩臺server時,為什么zookeeper集群會出問題。
首先,客戶端發(fā)起請求,2個server復(fù)制數(shù)據(jù)后即返回客戶端接收成功。
就在此刻,很不幸,在繼續(xù)同步更新給其他節(jié)點前,剛剛兩個復(fù)制了數(shù)據(jù)的節(jié)點掛了。
此時會怎樣呢?如下圖:
可以看到創(chuàng)建/z的操作在zookeeper集群中丟失了。
相信通過以上講解,你已經(jīng)能夠理解為什么法定人數(shù)一定要多于一半服務(wù)器的數(shù)量。
此外,我們要盡量選用奇數(shù)個服務(wù)器,這樣集群能容忍崩潰服務(wù)器占比更大,性價比更高。例如4臺服務(wù)器的集群,法定人數(shù)最少為3,那么只能允許1臺服務(wù)器崩潰,也就是僅允許25%的機器崩潰。而5臺服務(wù)器的集群,法定人數(shù)最少也是3,但是此時允許2臺服務(wù)器崩潰。換句話講,40%的機器崩潰后還能工作。
仲裁模式下,負載均衡通過客戶端隨機選擇連接串中的某個服務(wù)器來實現(xiàn)。
1.4.5 會話
客戶端對zookeeper集群發(fā)送任何請求前,需要和zookeeper集群建立會話??蛻舳颂峤唤ozookeeper的所有操作均關(guān)聯(lián)在一個會話上。當(dāng)一個會話因某種原因終止時,會話期間創(chuàng)建的臨時節(jié)點將會消失。而當(dāng)當(dāng)前服務(wù)器的問題,無法繼續(xù)通信時,會話將被透明的轉(zhuǎn)移到另外一臺zookeeper集群的服務(wù)器上。
會話提供了順序保障。同一個會話中的請求以FIFO順序執(zhí)行。并發(fā)會話的FIFO順序無法保證。
1.4.6 會話狀態(tài)和生命周期
會話狀態(tài)有:
connecting、connected、closed、not_connected
創(chuàng)建會話時,需要設(shè)置會話超時這個重要的參數(shù)。如果經(jīng)過時間t后服務(wù)接受不到這個會話的任何消息,服務(wù)就會聲明會話過期??蛻舳藗?cè),t/3時間未收到任何消息,客戶端向服務(wù)器發(fā)送心跳消息,2t/3時間后,客戶端開始尋找其他服務(wù)器。此時他有t/3的時間去尋找,找不到的話,會話失效。
重連服務(wù)器時,只有更新大于客戶端的服務(wù)器才能被連接,以免連接到落后的服務(wù)器。zookeeper中通過更新建立的順序,分配事務(wù)標識符。只有服務(wù)器的事物標識符大于客戶端攜帶的標識符時,才可連接。
回顧總結(jié)
本章首先介紹了分布式系統(tǒng)及分布式系統(tǒng)面臨的問題,隨后介紹了zookeeper是以何機制來解決這些問題的。最后介紹了zookeeper中的重要概念,在開始后續(xù)學(xué)習(xí)前,一定要確保自己理解了這些重要的概念,本章知識是后面章節(jié)學(xué)習(xí)的重要基石,希望大家以后多多支持腳本之家!
相關(guān)文章
Spring中的spring.factories文件用法(Spring如何加載第三方Bean)
這篇文章主要介紹了Spring中的spring.factories文件用法(Spring如何加載第三方Bean),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10SpringBoot項目沒有把依賴的jar包一起打包的問題解決
這篇文章主要介紹了SpringBoot項目沒有把依賴的jar包一起打包的問題解決,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-09-09使用@PathVariable注解如何實現(xiàn)動態(tài)傳值
這篇文章主要介紹了使用@PathVariable注解如何實現(xiàn)動態(tài)傳值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10