摘要:有可能是宕機(jī)或負(fù)荷嚴(yán)重的情況導(dǎo)致的。為分布式系統(tǒng)提供了協(xié)調(diào)功能和控制沖突。
背景
隨著計算機(jī)的硬件和操作系統(tǒng)兩者相輔相成地發(fā)展,從早期的ENIAC計算機(jī)到現(xiàn)在的x86的計算機(jī),從以前的單一控制終端(Single Operator, Single Console, SOSC)的操作系統(tǒng)到現(xiàn)在百花爭鳴的操作系統(tǒng)(如MacOS、Windows、Linux等),現(xiàn)代的操作系統(tǒng)發(fā)展還有一個最重要的特征是網(wǎng)絡(luò)的出現(xiàn),網(wǎng)絡(luò)促進(jìn)了網(wǎng)絡(luò)操作性系統(tǒng)和分布式操作系統(tǒng)的出現(xiàn)。對于網(wǎng)絡(luò)操作系統(tǒng)來說,其任務(wù)是將多個計算機(jī)虛擬成一個計算機(jī)。傳統(tǒng)的網(wǎng)絡(luò)操作系統(tǒng)是在現(xiàn)有操作系統(tǒng)的基礎(chǔ)上增加網(wǎng)絡(luò)功能,而分布式操作系統(tǒng)則是從一開始就把對多計算機(jī)的支持考慮進(jìn)來,是重新設(shè)計的操作系統(tǒng),所以比網(wǎng)絡(luò)操作系統(tǒng)效率高。分布式操作系統(tǒng)除了提供傳統(tǒng)操作系統(tǒng)的功能外,還提供多計算機(jī)協(xié)作的功能。
根據(jù)上述的發(fā)展,我們在其基礎(chǔ)上構(gòu)建的應(yīng)用也分為集中式系統(tǒng)和分布式系統(tǒng)。集中式系統(tǒng)具有明顯的單點問題。大型主機(jī)雖然在性能和穩(wěn)定性方面表現(xiàn)卓越,但這不代表它永遠(yuǎn)不會出問題。另外隨著業(yè)務(wù)的不斷發(fā)展,用戶訪問迅速提高,計算機(jī)系統(tǒng)的規(guī)模也在不斷擴(kuò)大,在單一大型主機(jī)上進(jìn)行的系統(tǒng)的擴(kuò)容往往比較困難。
按照正常的套路來說,下一步應(yīng)該是說隨著PC的性能不斷提升和網(wǎng)絡(luò)技術(shù)的快速普及,所以很多企業(yè)就開始去掉大型機(jī),從而改用普通PC來作為分布式的計算機(jī)系統(tǒng)。這樣說就太平淡了,企業(yè)也不是這么容易說變就變,就算谷歌開始的時候敢用可靠性較低的服務(wù)器的一個重要原因,在于它是最初并不為用戶提供存儲和計算服務(wù),服務(wù)器都是自己內(nèi)部使用。如果計算到一半死機(jī)了,那就再從死機(jī)的斷點重新啟動計算。甚至如果一開始有些中間數(shù)據(jù)丟失了,那就再產(chǎn)生一遍。這時它的可靠性不像銀行那樣重要。2004年當(dāng)谷歌開始提供Gmail服務(wù),為了確保用戶的數(shù)據(jù)不丟失,它采用了3x3九臺服務(wù)器存一組數(shù)據(jù),這個成本就不低了。同時期,雅虎等公司采用兩臺可靠性的服務(wù)器存儲郵件。這時期,谷歌的Gmail是非常賠錢的。后來谷歌的云存儲部門用軟件實現(xiàn)了5臺服務(wù)器分布式存儲,達(dá)到過去九臺服務(wù)器的可靠性,Gmail的成本才降下來。谷歌想方設(shè)法用廉價服務(wù)器集群取代超級計算機(jī)的過程遠(yuǎn)比很多人想象的復(fù)雜。這種想法其實早在谷歌之前就有了,但是其它公司就是因為無法解決其中的很多技術(shù)細(xì)節(jié)問題,使得采用大量低可靠性的服務(wù)器帶來的好處還沒有它帶來的麻煩多,最終都放棄了。(這里引申出谷歌的文化之一)谷歌把事做到了極致,克服了各種技術(shù)困難,最后做成了。
企業(yè)吸收已證實可行的經(jīng)驗是很快速,于是后來亞馬遜等公司也實現(xiàn)了廉價服務(wù)器集群取代超級計算機(jī)的功能。其中在國內(nèi),最為典型的就是阿里巴巴的“去IOE”活動,
也正是在這個大背景之下,還有在大數(shù)據(jù)和云計算驅(qū)動之下,ZooKeeper在雅虎里誕生了。用于解決很多分布式系統(tǒng)下的問題。當(dāng)設(shè)計一個使用ZooKeeper的應(yīng)用時,最好就是將應(yīng)用數(shù)據(jù)和協(xié)調(diào)數(shù)據(jù)給分開。如郵件系統(tǒng),用戶只關(guān)心他們的郵箱內(nèi)容,而不需要知道哪臺郵箱服務(wù)器在處理。因此在這里郵箱內(nèi)容就是應(yīng)用數(shù)據(jù),而協(xié)調(diào)數(shù)據(jù)則是具體是哪臺郵件服務(wù)器在處理。
分布式的問題由于分布式系統(tǒng)概念有很多種,在這里我們定義為:一個系統(tǒng)由多個組件組成,而每個組件獨立并同時運行在不同的物理機(jī)器上。
分布式系統(tǒng)一誕生就面臨諸多的難題和挑戰(zhàn)。典型的問題有這些:
通信失敗(Communication Failure)
由于分布系統(tǒng)引入了網(wǎng)絡(luò)因素,而網(wǎng)絡(luò)本身是不穩(wěn)定的。因此分布式系統(tǒng)中各個結(jié)點之前進(jìn)網(wǎng)絡(luò)通信時,會出現(xiàn)消息丟失和消息延遲等現(xiàn)象。
網(wǎng)絡(luò)分區(qū)(Network Partion)
網(wǎng)絡(luò)分區(qū),也稱腦裂(split-brain),集群中部分節(jié)點之間不可達(dá)而引起的(或者因為節(jié)點請求壓力較大,導(dǎo)致其他節(jié)點與該節(jié)點的心跳檢測不可用)。當(dāng)上述情況發(fā)生時,不同分裂的小集群會自主的選擇出master節(jié)點,造成原本的集群會同時存在多個master節(jié)點。
三態(tài)
因為通信失敗的問題,會帶來其中一個問題是三態(tài),三態(tài)即成功、失敗與超時。分布式系統(tǒng)的每一次請求與響應(yīng)都會有這三種結(jié)果。最麻煩的是超時,因為它存在這兩種可能:
1.由于通訊失敗,該請求(消息)并沒有被成功地發(fā)送到接收方,而是在發(fā)送過程中就丟失了。
2.該請求(消息)成功地被接收方接收后,并進(jìn)行了處理,但是在將響應(yīng)反饋給發(fā)送方時,發(fā)生了消息丟失現(xiàn)象。
節(jié)點故障
這也是屬于通信失敗的情況,但著重點是說,機(jī)器自身掛了,無法發(fā)出消息。有可能是宕機(jī)或負(fù)荷嚴(yán)重的情況導(dǎo)致的。
上述分布式問題導(dǎo)致了一致性問題難以解決,而且在2002年的時候,MIT的Seth Gibert和Nancy Lynch證明的CAP定理。這個定理是:系統(tǒng)不可能同時滿足一致性(Consistency)、可用性(Availability)和分區(qū)容錯性(Partition tolerance)。
既然是邏輯證明出來的,那它就一定對的。這也是分布式系統(tǒng)的邊界或者說是上限。所以人們開始在一致性和可用性上權(quán)衡,從而誕生很多算法如2PC、3PC和Paxos算法,這些都影響著ZooKeeper的設(shè)計。
ZooKeeper不能解決所有分布式系統(tǒng)的問題。但它提供了一個很好框架去處理這些問題。
ZooKeeper為分布式系統(tǒng)提供了協(xié)調(diào)功能和控制沖突。協(xié)調(diào)意思是說幾個進(jìn)程需要一起做一些事情,例如,在Master-Worker分布式架構(gòu)里,worker通知master它可用,master因此分派任務(wù)給它。
而控制沖突則不一樣:它更多是這種情形,兩個進(jìn)程是不能同時進(jìn)行,一個必須等待一個才能進(jìn)行。例如,在Master-Worker分布式架構(gòu)里,我們希望只有一個進(jìn)程變成master,所以當(dāng)多個進(jìn)程同時激活自己為master時,必須實現(xiàn)互斥( mutual exclusion),只能有一個進(jìn)程獲得鎖,并成為master角色。
ZooKeeper的API提供了:
強(qiáng)一致性,順序和持久化的保證
提供實現(xiàn)同步的能力
一個更簡單的方法處理分布式系統(tǒng)中的并發(fā)問題。
ZooKeeper的基礎(chǔ)我們將圍繞一個例子去講述概念并實踐。這個例子是:Master-Worker分布式架構(gòu)。
架構(gòu)圖如下:
Master進(jìn)程的職責(zé)時跟蹤Worker和任務(wù),并將任務(wù)分派給Worker。為了實現(xiàn)這個Master-Worker系統(tǒng),我們必須解決這三個關(guān)鍵問題:
Master故障
如果master故障并且變得不可用,系統(tǒng)就不能分配新任務(wù)或重新分配失敗的任務(wù)。
Woker故障
如果worker故障,則分配給它的任務(wù)則完成不了。
通信失敗
如果master和worker不能交換信息,則worker可能無法獲取分派給它的任務(wù)。
為了解決上述問題,這個系統(tǒng)必須有以下功能:能在一個master掛掉之后,重新選擇一個新的master;判斷那些woker是可用的;當(dāng)worker與master因為網(wǎng)絡(luò)分區(qū)失去與master連接時能夠重新分派任務(wù);當(dāng)一個節(jié)點獲得鎖之后發(fā)生網(wǎng)絡(luò)分區(qū)或掛掉時,需讓這個鎖失效。
重新分配任務(wù)有以下情況:如果任務(wù)可重復(fù)執(zhí)行,則可以無需任何校驗的把這任務(wù)重新分派。但如果這個任務(wù)是不能重復(fù)執(zhí)行的,則需要協(xié)調(diào)多個worker執(zhí)行任務(wù)的情況。znode
ZooKeeper并沒有直接提供上述的功能,而是提供一個跟文件系統(tǒng)很像的API。這個被組織稱樹結(jié)構(gòu),每個結(jié)點都存儲很小的數(shù)據(jù)的結(jié)點(不超過1M),被稱為znode。如下圖,根結(jié)點包含4個結(jié)點,其中3個又是分支結(jié)點,擁有葉子節(jié)點。而葉子結(jié)點就包含數(shù)據(jù)。
不存在的結(jié)點在znode里也是蘊(yùn)含信息。如在這個Master-Worker例子里,如果master結(jié)點不存在,則表明master還沒選出來。另外根據(jù)上圖,我們可以看出/workers結(jié)點是當(dāng)前系統(tǒng)中所有可用的woker的父節(jié)點。foo.com:2181是worker的信息。如果woker不可用了,就應(yīng)該把對于的結(jié)點從/worers上刪除。
而/tasks結(jié)點是父結(jié)點,其子結(jié)點是已經(jīng)被創(chuàng)建的任務(wù),且等待被執(zhí)行。Master-Worker例子里,客戶端就可以在/tasks下創(chuàng)建一個結(jié)點來代表一個新任務(wù),并且等待該任務(wù)的狀態(tài)。
最后/assign結(jié)點的子結(jié)點是所有已經(jīng)被分派給worker的任務(wù)。
znode可以有和可以沒有數(shù)據(jù)。如果有數(shù)據(jù),數(shù)據(jù)類型必須是字節(jié)數(shù)據(jù)(Byte Array)。字節(jié)數(shù)組的含義解釋就需要各自應(yīng)用決定,ZooKeeper沒有提供解析這字節(jié)數(shù)組的功能。
ZooKeeper的命令行提供了一下API:
create /path data
創(chuàng)建一個znode結(jié)點名為/path,包含數(shù)據(jù)data
delete /path
刪除znode結(jié)點/path
exists /path
檢查是否有/path結(jié)點
setData /path data
設(shè)置/path結(jié)點的數(shù)據(jù)
getData /path
獲取/path結(jié)點數(shù)據(jù)
getChildren /path
獲取/path結(jié)點的子結(jié)點列表
znode的模式在創(chuàng)建znode結(jié)點時,我們可以指定模式(mode),不同模式?jīng)Q定znode結(jié)點的不同行為:
永久(Persistent)和臨時(Ephemeral)znode結(jié)點znode結(jié)點只能是永久結(jié)點或者是臨時結(jié)點。永久結(jié)點/path只能被delete命令刪除。而臨時結(jié)點,在創(chuàng)建該結(jié)點的客戶端故障了或失去與ZooKeeper失去連接時,這個結(jié)點會被刪除。
在Master-Worker例子里,我們需要維護(hù)任務(wù)的分派情況,哪怕master故障了。
臨時znode結(jié)點傳遞著這樣的信息:結(jié)點的創(chuàng)建者的session有效,則結(jié)點的應(yīng)用才能存在。例如,master的結(jié)點在Master-Worker例子里就是臨時的。master故障時,master結(jié)點也不應(yīng)該存在。同樣的也適合worker的情況。
因為臨時znode結(jié)點在它的創(chuàng)建者的session超時失效時被刪除,則我們不允許臨時結(jié)點擁有子結(jié)點。
一個znode可以被設(shè)置為序列(sequential)。一個序列結(jié)點時唯一的,單調(diào)遞增的整數(shù)。是在path后面追加序列數(shù)據(jù)。例如,如果一個客戶端創(chuàng)建一個序列znode結(jié)點/task/task-,ZooKeeper會分配一個序列,如1,追加到路徑上,則為/task/task-1。序列znode結(jié)點提供一個簡便地方法去創(chuàng)建擁有唯一名字的znode結(jié)點。也可以被用來查看創(chuàng)建znode結(jié)點的順序。
總結(jié)因此總的來說,znode有以下四個模式:persistent, ephemeral, persistent_sequential和ephemeral_sequential。
Watch與通知(Watches and Notifications)由于是遠(yuǎn)程訪問ZooKeeper,所以訪問znode結(jié)點是非常昂貴的:高延遲或多無用的操作。考慮以下情況,如果下圖,第二次使用getChildren /task返回的是同樣的值,因此時沒有必要的。
這是輪訓(xùn)(polling)的普遍出現(xiàn)的問題。我們使用一種叫通知(notifications)的機(jī)制來代理客戶端的輪訓(xùn):客戶端在ZooKeeper上注冊接收znode結(jié)點變化的通知。指定一個znode結(jié)點,接受其一個通知的注冊,這過程叫 設(shè)置watch。一個watch是單步操作(one-shot operation),也就是說一個watch僅僅觸發(fā)一個通知。如果想接受多個通知,則需要在接受到通知后,重新設(shè)置watch。設(shè)置watch和接受通知的過程如下圖:
版本(Version)每一個znode結(jié)點都會有一個版本號,這個版本號在每次結(jié)點的數(shù)據(jù)發(fā)生改變都會遞增。這樣ZooKeeper的一些API操作就可以帶上條件,這操作如setData和Delete。設(shè)置數(shù)據(jù)時可以帶上版本號,版本號匹配不上則操作失敗。操作過程如下圖:
ZooKeeper的架構(gòu)客戶端可以通過client庫來與ZooKeeper節(jié)點進(jìn)行通信。
ZooKeeper服務(wù)可以有兩種模式:單機(jī)(standalone)和法定人數(shù)(quorum)。單機(jī)模式也就是單個服務(wù)器,ZooKeeper的狀態(tài)不會被復(fù)制(replicate)。而在法定人數(shù)模式下,則會有一組ZooKeeper服務(wù)器,也稱為ZooKeeper套裝(ZooKeeper ensemble),節(jié)點之間會復(fù)制ZooKeeper的狀態(tài),并一起提供服務(wù)。
在法定人數(shù)模式下,ZooKeeper會冗余(replicate)它的數(shù)據(jù)到各個服務(wù)器里。當(dāng)如果客戶端必須等待每個服務(wù)器都保持好它的數(shù)據(jù),才能往下進(jìn)行操作,則延遲將會難以接受。在現(xiàn)實世界的公共事務(wù)管理里,法定人數(shù)是要求出席投票的最低人數(shù)。而在ZooKeeper里,法定人數(shù)是保證ZooKeeper能夠正常工作,可用,的最少服務(wù)器數(shù)量。這個最少數(shù)量也是保證至少有這么多臺服務(wù)器是同步了數(shù)據(jù)的。這樣才能保證數(shù)據(jù)是被安全保存。如我們用5臺ZooKeeper服務(wù)器,則法定人數(shù)則為3臺。只要有3臺服務(wù)器保存了數(shù)據(jù),客戶端則可以往下繼續(xù)自己的事情,另外兩天服務(wù)器最終會同步剛剛保存的數(shù)據(jù)。
會話(Session)客戶端在對ZooKeeper發(fā)送任何請求之前,都得先與ZooKeeper建立會話(session)。會話這個概念在ZooKeeper里是非常重要且關(guān)鍵的。所有操作都必須關(guān)聯(lián)著會話。當(dāng)一個會話因為某些原因結(jié)束時,通過這會話創(chuàng)建的臨時結(jié)點也將會伴隨這會話的結(jié)束而被刪除。
客戶端是通過TCP連接來與服務(wù)器通信,客戶端只能連接一個服務(wù)器。如果客戶端連接的一臺服務(wù)器掛了,則session將會移動到下一臺服務(wù)器,這個移動過程是透明的,由ZooKeeper負(fù)責(zé)處理的。
會話提供了有序保證(order guarantees),意思是一個會話里的操作都是FIFO的順序。但跨session操作時,哪怕session不是重疊,而是連續(xù)的不同的session,這個FIFO的順序也會被破壞,如:
客戶端建立一個session,并異步的連續(xù)的發(fā)送兩個操作,create /tasks和/workers.
第一個session失效
客戶端建立其他session,也發(fā)送一個異步請求,create /assign
在這種情況下,是有可能僅僅只有/tasks和/assign被創(chuàng)建,/workers而沒有被創(chuàng)建。這是因為被交錯的會話給打斷了。
ZooKeeper的命令行體驗通過命令行來操作ZooKeeper來實現(xiàn)Master-Worker這個例子。
安裝ZooKeeper(quorum模式)1.下載安裝包zookeeper-3.4.5.tar.gz
2.解壓zookeeper-3.4.5.tar.gz到/usr/local
tar -zxvf zookeeper-3.4.5.tar.gz
3.創(chuàng)建zoo.cfg配置文件
mv conf/zoo_sample.cfg conf/zoo.cfg
4.修改zoo.cfg配置文件,修改如下
#防止把根分區(qū)給被占滿 dataDir=/usr/local/zookeeper-3.4.5/tmp clientPort=2181 server.1=zk1.jevoncode.com:2888:3888 server.2=zk2.jevoncode.com:2888:3888 server.3=zk3.jevoncode.com:2888:3888
其中2181是ZooKeeper服務(wù)器的開放給客戶端訪問的端口,而2888和3888是ZooKeeper節(jié)點之間用于通信和leader選舉。
5.在各個結(jié)點的/usr/local/zookeeper-3.4.5/tmp目錄下建立myid文件,內(nèi)容為server的id,如給zk1.jevoncode.com這個結(jié)點添加myid
echo "1">/usr/local/zookeeper-3.4.5/tmp/myid
6.在每個結(jié)點啟動ZooKeeper服務(wù)
./bin/zkServer.sh start
7.查看每個結(jié)點的狀態(tài),就可以看到leader和follower了
./bin/zkServer.sh status實現(xiàn)Master-Worker這個例子
我們通過zkCli.sh工具來實現(xiàn)Master-Worker這個例子的功能。
Master-Worker這個例子涉及到三個角色:
Master
這個master負(fù)責(zé)監(jiān)控新woker和任務(wù),并分派任務(wù)給可用的worker。
Worker
worker將自己注冊到該系統(tǒng)中,并保證master能夠看到它是可用的,可執(zhí)行任務(wù),然后監(jiān)聽新任務(wù)。Master角色
客戶端
客戶端創(chuàng)建新任務(wù)并等待系統(tǒng)的響應(yīng)
1.在窗口A連接ZooKeeper
./bin/zkCli.sh -server zk1.jevoncode:2181,zk2.jevoncode:2181,zk3.jevoncode:2181
2.在窗口A創(chuàng)建臨時znode結(jié)點/master
create -e /master "master1.jevoncode.com:2223"
3.在窗口B連接ZooKeeper
./bin/zkCli.sh -server zk1.jevoncode:2181,zk2.jevoncode:2181,zk3.jevoncode:2181
4.在窗口B創(chuàng)建臨時znode結(jié)點/master,模擬兩個進(jìn)程激活master角色。這個會失敗
#會返回Node already exists: /master create -e /master "master2.jevoncode.com:2223"
5.在窗口B,既然/master已存在,則需監(jiān)聽/master,以便當(dāng)窗口A的master故障時,能快速恢復(fù)過來。stat命令是獲取znode屬性,然后后續(xù)參數(shù)true是設(shè)置一個watch在/master上
stat /master true
6.當(dāng)窗口A退出時,窗口B會收到一下通知:
WatchedEvent state:SyncConnected type:NodeDeleted path:/master
7.此時窗口B就可以激活自己稱為master
create -e /master "master2.jevoncode.com:2223"Worker角色、任務(wù)和任務(wù)分派
在繼續(xù)客戶端和Worker操作之前,我們需要創(chuàng)建三個父節(jié)點,/workers, /tasks和/assign
create /workers "" create /tasks "" create /assign "" ls /
另外Master角色需監(jiān)聽worker和任務(wù)
ls /workers true ls /tasks trueWorker角色
1.在窗口C連接ZooKeeper
./bin/zkCli.sh -server zk1.jevoncode:2181,zk2.jevoncode:2181,zk3.jevoncode:2181
2.創(chuàng)建一個worker結(jié)點
create -e /workers/worker1.jevoncode.com "worker1.jevoncode.com:2224"
3.此時Master角色的窗口會受到通知
WATCHER:: WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/workers
4.worker需創(chuàng)建一個父節(jié)點來接受任務(wù)分派,并watch
create /assign/worker1.jevoncode.com "" ls /assign/worker1.jevoncode.com true客戶端角色
1.在窗口D連接ZooKeeper
./bin/zkCli.sh -server zk1.jevoncode:2181,zk2.jevoncode:2181,zk3.jevoncode:2181
2.提交任務(wù),在這里是創(chuàng)建一個序列znode結(jié)點。
create -s /tasks/task- "cmd"
3.設(shè)置一個watch,等待任務(wù)完成
ls /task/task-0000000000 true
4.當(dāng)任務(wù)創(chuàng)建是,Master的窗口將會收到通知
WATCHER:: WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/tasks
5.此時Master窗口查看任務(wù),查看可用worker并分派任務(wù)
ls /tasks ls /workers create /assign/worker1.jevoncode.com/task-0000000000 ""
6.此時Worker窗口C收到通知
WATCHER:: WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/assign/worker1.example.com
7.Worker窗口C,查看任務(wù),并執(zhí)行完任務(wù)后修改任務(wù)狀態(tài)
ls /assign/worker1.jevoncode.com create /tasks/task-0000000000/status "done"
8.客戶端收到通知,并查看任務(wù)完成結(jié)果
#通知 WATCHER:: WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/tasks/task-0000000000 #查看結(jié)果 get /tasks/task-0000000000
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/69195.html
閱讀 813·2023-04-25 22:57
閱讀 3062·2021-11-23 10:03
閱讀 624·2021-11-22 15:24
閱讀 3169·2021-11-02 14:47
閱讀 2912·2021-09-10 11:23
閱讀 3130·2021-09-06 15:00
閱讀 3953·2019-08-30 15:56
閱讀 3337·2019-08-30 15:52