這篇文章主要講解了“分布式協(xié)調(diào)服務(wù)組件Zookeeper的必備知識(shí)點(diǎn)有哪些”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“分布式協(xié)調(diào)服務(wù)組件Zookeeper的必備知識(shí)點(diǎn)有哪些”吧!
成都網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁設(shè)計(jì)、成都網(wǎng)站建設(shè)、微信開發(fā)、微信小程序開發(fā)、集團(tuán)成都企業(yè)網(wǎng)站定制等服務(wù)項(xiàng)目。核心團(tuán)隊(duì)均擁有互聯(lián)網(wǎng)行業(yè)多年經(jīng)驗(yàn),服務(wù)眾多知名企業(yè)客戶;涵蓋的客戶類型包括:成都自上料攪拌車等眾多領(lǐng)域,積累了大量豐富的經(jīng)驗(yàn),同時(shí)也獲得了客戶的一致贊譽(yù)!
Zookeeper是一個(gè)開源的分布式協(xié)調(diào)服務(wù)組件。是分布式集群的監(jiān)控者,能對(duì)集群中節(jié)點(diǎn)的狀態(tài)變更進(jìn)行及時(shí)的反饋。
Zookeeper對(duì)分布式一致性的保證:
順序一致性:保證服務(wù)端的執(zhí)行順序是按照客戶端操作的發(fā)送順序來執(zhí)行的
原子性:客戶端操作結(jié)果要么成功、要么失敗。
單一視圖:無論客戶端連接到那個(gè)服務(wù)端,看到的視圖是一致的。
可靠性:客戶端操作請(qǐng)求一定執(zhí)行成功,則執(zhí)行結(jié)果會(huì)一直持久到下一次操作
最終一致性:保證一定時(shí)間內(nèi)客戶端看到的視圖是最新的數(shù)據(jù)(允許一定時(shí)間內(nèi)看到的數(shù)據(jù)不是最新的)
Zookeeper默認(rèn)保證客戶端拿到的視圖是連接到的服務(wù)器此時(shí)的順序一致性視圖,通過客戶端調(diào)用SyncCommand保證是客戶端看到的視圖是此時(shí)集群中所有節(jié)點(diǎn)的最新強(qiáng)一致性視圖。
注意:最終一致性存在在一段時(shí)間內(nèi)客戶端看到的視圖不一定是最新數(shù)據(jù)
ZooKeeper -server host:port -client-configuration properties-file cmd args addWatch [-m mode] path # optional mode is one of [PERSISTENT, PERSISTENT_RECURSIVE] - default is PERSISTENT_RECURSIVE addauth scheme auth close config [-c] [-w] [-s] connect host:port create [-s] [-e] [-c] [-t ttl] path [data] [acl] delete [-v version] path deleteall path [-b batch size] delquota [-n|-b] path get [-s] [-w] path getAcl [-s] path getAllChildrenNumber path getEphemerals path history listquota path ls [-s] [-w] [-R] path printwatches on|off quit reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,...]*]] | [-add serverId=host:port1:port2;port3[,...]]* [-remove serverId[,...]*] redo cmdno removewatches path [-c|-d|-a] [-l] set [-s] [-v version] path data setAcl [-s] [-v version] [-R] path acl setquota -n|-b val path stat [-w] path sync path version whoami
Zookeeper數(shù)據(jù)是存儲(chǔ)在內(nèi)存中的具有層次結(jié)構(gòu)的命名空間,類似于多級(jí)別的文件目錄結(jié)構(gòu)一樣,不同的是每個(gè)命名空間可以掛載數(shù)據(jù)。命名空間不應(yīng)該使用以下字符:\u0000、\u0001-\u001F、\u007F、\ud800-uF8FF、\uFFF0-uFFFF、保留關(guān)鍵字:zookeeper。命名空間都是絕對(duì)路徑,不存在相對(duì)路徑,以斜杠路徑分隔符劃分層級(jí)。
/meta1 /meta2 |-- /meta1/node1 |-- /meta2/node1 |-- /meta1/node2 |--|--/meta2/node2/child |--|-- /meta1/node3/child |-- /meta2/node3 |-- /meta1/node4 |-- /meta2/node4
Zookeeper是Java語言開發(fā)的,節(jié)點(diǎn)稱為Znode,樹形存儲(chǔ)結(jié)構(gòu)對(duì)象為:DataTree,節(jié)點(diǎn)存儲(chǔ)對(duì)象為:ConcurrentHashMap<String, DataNode> nodes。
Zookeeper節(jié)點(diǎn)本身存儲(chǔ)無大小限制,但是數(shù)據(jù)序列化傳輸時(shí)有限制,默認(rèn)上限1M,可通過系統(tǒng)屬性:"jute.maxbuffer"配置,詳細(xì)可見類:BinaryInputArchive。但是Zookeeper在集群中節(jié)點(diǎn)數(shù)據(jù)傳輸較多,不建議存儲(chǔ)過大的數(shù)據(jù),一旦修改該上限,集群中所有節(jié)點(diǎn)都要修改。
Zookeeper支持四種類型的節(jié)點(diǎn),節(jié)點(diǎn)只能逐個(gè)創(chuàng)建:v3.5.3+支持對(duì)持久節(jié)點(diǎn)設(shè)置TTL
臨時(shí)節(jié)點(diǎn)(EPHEMERAL):節(jié)點(diǎn)生命周期綁定客戶端會(huì)話,在客戶端結(jié)束會(huì)話時(shí)會(huì)自動(dòng)刪除,節(jié)點(diǎn)下不能創(chuàng)建新的節(jié)點(diǎn)
臨時(shí)有序節(jié)點(diǎn):(EPHEMERAL_SEQUENTIAL):具有臨時(shí)節(jié)點(diǎn)特點(diǎn),節(jié)點(diǎn)創(chuàng)建后節(jié)點(diǎn)名自動(dòng)追加序號(hào),多次創(chuàng)建序號(hào)自增,10位數(shù)字
持久節(jié)點(diǎn)(PERSISENT):持久落盤保存,重啟不丟失,需要手動(dòng)調(diào)用delete命令刪除
持久有序節(jié)點(diǎn)(PERSISTENT_SEQUENTIAL):具有持久節(jié)點(diǎn)特點(diǎn),節(jié)點(diǎn)創(chuàng)建后節(jié)點(diǎn)名會(huì)自動(dòng)追加序號(hào),多次創(chuàng)建序號(hào)自增,10位數(shù)字
容器節(jié)點(diǎn)(CONTAINER):當(dāng)其子節(jié)點(diǎn)被完全刪除時(shí),該節(jié)點(diǎn)會(huì)自動(dòng)刪除
限時(shí)節(jié)點(diǎn)(TTL):對(duì)持久節(jié)點(diǎn)增加限時(shí)時(shí)長(zhǎng),默認(rèn)禁用該功能,啟用屬性為:zookeeper.extendedTypesEnabled
Zookeeper在分布式協(xié)調(diào)中遵循ZAB協(xié)議,具有過半有效特點(diǎn),要求集群節(jié)點(diǎn)數(shù)量符合:2N+1。也就是可以單機(jī)部署,集群部署最少3個(gè)節(jié)點(diǎn),最多宕機(jī)1臺(tái),否則集群不可用(永遠(yuǎn)不會(huì)過半,也不會(huì)轉(zhuǎn)為單機(jī)運(yùn)行,會(huì)報(bào)錯(cuò)),這里也說明Zookeeper保證的是分布式中CAP定理中的CP,一致性和分區(qū)容錯(cuò)性。注意:Zookeeper集群中最少時(shí)2臺(tái)機(jī)器就可以運(yùn)行了,但是宕機(jī)任意一臺(tái)集群就不可用。
在Zookeeper啟動(dòng)同時(shí)會(huì)啟動(dòng)一個(gè)線程輪詢服務(wù)器狀態(tài),每臺(tái)集群節(jié)點(diǎn)服務(wù)只會(huì)存在以下狀態(tài):
LOOKING:表示當(dāng)前集群中不存在Leader節(jié)點(diǎn),集群中所有可參與選舉的節(jié)點(diǎn)正在進(jìn)行Leader節(jié)點(diǎn)選舉
LEADING:表示該節(jié)點(diǎn)是集群中的領(lǐng)導(dǎo)者leader
leader角色具有對(duì)外提供讀寫視圖能力,集群中只有Leader節(jié)點(diǎn)能執(zhí)行事務(wù)(增、刪、改)操作,其他節(jié)點(diǎn)都需要轉(zhuǎn)發(fā)到Leader來執(zhí)行
FOLLOWING:表示該節(jié)點(diǎn)是集群中的跟隨者follower,說明當(dāng)前集群中存在某個(gè)其他節(jié)點(diǎn)已經(jīng)是Leader節(jié)點(diǎn)。
follower角色具有同步視圖數(shù)據(jù)、轉(zhuǎn)發(fā)事務(wù)請(qǐng)求到Leader以及對(duì)外提供讀視圖能力,在Zookeeper中配置為participant:參與者
OBSERVING:表示該節(jié)點(diǎn)是集群中的觀察者Observer
Observer角色主要擴(kuò)展集群的非事務(wù)處理能力,對(duì)外提供讀視圖能力、轉(zhuǎn)發(fā)事務(wù)請(qǐng)求到Leader,不具有選舉能力。
observer與Follower統(tǒng)稱為learner
二階段提交是為保證分布式系統(tǒng)中數(shù)據(jù)的一致性而將事務(wù)操作行為分為兩個(gè)階段來完成。
第一階段:leader服務(wù)節(jié)點(diǎn)將該事務(wù)請(qǐng)求是否能夠執(zhí)行通過提議方式詢問集群中的所有Follower節(jié)點(diǎn),各個(gè)Follower節(jié)點(diǎn)對(duì)該提議進(jìn)行確認(rèn)ACK。
第二階段:Leader對(duì)收到的ACK進(jìn)行過半判斷,判定成功則進(jìn)行事務(wù)提交,異步通知各Follower節(jié)點(diǎn)也進(jìn)行事務(wù)提交。如果判定失敗則進(jìn)行事務(wù)回滾。
事務(wù)一般都是通過日志文件的形式來實(shí)現(xiàn)的,zookeeper也不例外。第一階段針對(duì)事務(wù)請(qǐng)求會(huì)先進(jìn)行日志記錄,然后發(fā)出詢問。在第二階段收到各Follower節(jié)點(diǎn)確認(rèn)后,根據(jù)之前的日志,更新數(shù)據(jù)庫來完成事務(wù)。
主要指2兩種運(yùn)行狀態(tài),在Leader失聯(lián)時(shí)進(jìn)入崩潰恢復(fù)狀態(tài),進(jìn)行Leader選舉,當(dāng)選舉完成后進(jìn)入原子廣播協(xié)議,Leader節(jié)點(diǎn)將事務(wù)請(qǐng)求廣播到Follower節(jié)點(diǎn)
Zookeeper集群中是必須要一臺(tái)Leader角色的節(jié)點(diǎn)的,在不存在Leader節(jié)點(diǎn)時(shí)就需要集群內(nèi)部自行選舉,這種情況分為兩種:1:集群首次啟動(dòng)時(shí),2:集群中Leader節(jié)點(diǎn)失聯(lián)時(shí),二者的選舉流程一致,只是前者時(shí)啟動(dòng)時(shí)發(fā)現(xiàn)Leader不存在,后者是運(yùn)行時(shí)發(fā)現(xiàn)Leader失聯(lián)。
Zookeeper節(jié)點(diǎn)啟動(dòng)時(shí)默認(rèn)的狀態(tài)為:LOOKING,即集群中不存在Leader節(jié)點(diǎn),需要進(jìn)行Leader選舉。對(duì)于節(jié)點(diǎn)狀態(tài)的監(jiān)控是一個(gè)死循環(huán),隨服務(wù)啟動(dòng)而開始,隨服務(wù)停止而結(jié)束,因此首次啟動(dòng)會(huì)立馬根據(jù)選舉算法開始Leader選舉,v3.5+選舉算法只有一個(gè): FastLeaderElection
在Zookeeper的選票中存在四個(gè)很重要的數(shù)據(jù):myid(集群服務(wù)ID)、zxid(事務(wù)ID)、peerEpoch(leader輪期)、electionEpoch(選舉輪次)。
myid表示集群節(jié)點(diǎn)的服務(wù)唯一ID,這是配置文件中配置的ID,是身份識(shí)別碼。
zxid表示集群節(jié)點(diǎn)中記錄的事務(wù)日志ID,是一個(gè)64位Long類型數(shù)字,其中高32位為epoch,低32位為自增計(jì)數(shù)器counter。
epoch則表示當(dāng)前集群中Leader所處的輪期,每次Leader變更都會(huì)自增1,是Zxid的高32位。
electionEpoch表示自節(jié)點(diǎn)啟動(dòng)時(shí)集群中進(jìn)行過Leader選舉的輪次,每次Leader選舉內(nèi)部多個(gè)輪次
zxid結(jié)構(gòu)如下
public static long makeZxid(long epoch, long counter) { return (epoch << 32L) | (counter & 0xffffffffL); }
zxid整體是自增的,Zookeeper通過自增來保證數(shù)據(jù)的順序性。根據(jù)Zookeeper保證的順序一致性:zxid越大則該事務(wù)執(zhí)行的越晚,保存的數(shù)據(jù)越完整。
Leader選舉前后要保證的就是數(shù)據(jù)的盡可能一致性,因此Leader選舉的實(shí)質(zhì)就是找出存活的Follower節(jié)點(diǎn)中數(shù)據(jù)保存最完善的那一臺(tái)作為L(zhǎng)eader。
尋找方式就是比較zxid大小。如果zxid都一致,則說明存活的Follower節(jié)點(diǎn)數(shù)據(jù)保存完整性是一樣的,隨便選一臺(tái)都行,Zookeeper默認(rèn)選中myid最大的那一臺(tái)。
尋找算法則是FastLeaderElection的實(shí)現(xiàn)過程
在Leader選舉流程中,存在一個(gè)阻塞隊(duì)列,用來接收其他集群節(jié)點(diǎn)中發(fā)送的選票,而當(dāng)前節(jié)點(diǎn)也會(huì)持有一個(gè)選票。選票內(nèi)容主要包括:myid(集群節(jié)點(diǎn)身份識(shí)別唯一ID)、zxid(集群節(jié)點(diǎn)最大事務(wù)日志ID)、peerEpoch(集群節(jié)點(diǎn)事務(wù)所處輪期)、electionEpoch(集群已進(jìn)行Leader選舉的次數(shù))。
在選舉流程開始時(shí)首先將投向自己的選票(選票內(nèi)容均指向自身節(jié)點(diǎn)信息)發(fā)送出去,由于機(jī)器中節(jié)點(diǎn)在啟動(dòng)時(shí)都會(huì)發(fā)送一個(gè)選票,因此阻塞隊(duì)列就會(huì)收到其他節(jié)點(diǎn)的選票。選票的發(fā)送屬于異步發(fā)送,方法為:sendNotifications,先把內(nèi)容ToSend放入阻塞隊(duì)列,然后發(fā)送線程去逐個(gè)發(fā)送。
節(jié)點(diǎn)使用logicalclock(邏輯時(shí)鐘)來記錄當(dāng)前集群進(jìn)行Leader選舉的輪次electionEpoch。并在每次投票之后以輪次為基礎(chǔ)進(jìn)行流水記錄,便于后續(xù)分析,節(jié)點(diǎn)只記錄最新輪次的投票流水。
當(dāng)其他節(jié)點(diǎn)的electionEpoch大于當(dāng)前節(jié)點(diǎn)的electionEpoch時(shí),說明,當(dāng)前節(jié)點(diǎn)錯(cuò)過了某輪投票,需要更新logicalclock,并在選票PK之后更新自身持有的投票信息,為數(shù)據(jù)較為完整的節(jié)點(diǎn)投上一票。(說明之前的投票所處輪次已過時(shí),需要為最新輪次重新投一票)
// 選票PK方法 return ((newEpoch > curEpoch)|| ((newEpoch == curEpoch) && ((newZxid > curZxid)|| ((newZxid == curZxid)&& (newId > curId)))));
當(dāng)其他節(jié)點(diǎn)的electionEpoch小于當(dāng)前節(jié)點(diǎn)的electionEpoch時(shí),說明,其他節(jié)點(diǎn)錯(cuò)過了某輪投票,本身之前投票記錄是有效的,其他節(jié)點(diǎn)的投票所處輪次是過時(shí),因此舍棄這個(gè)過時(shí)投票,不參與統(tǒng)計(jì)。(本身的票已經(jīng)投出去了,不需要重復(fù)投票)
當(dāng)其他節(jié)點(diǎn)的electionEpoch等于當(dāng)前節(jié)點(diǎn)的electionEpoch時(shí),說明,兩者節(jié)點(diǎn)都是正常投票,則進(jìn)行選票PK,比較本節(jié)點(diǎn)的zxid、myid和選票節(jié)點(diǎn)的對(duì)應(yīng)值大小。哪個(gè)節(jié)點(diǎn)的值大則說明哪個(gè)節(jié)點(diǎn)記錄的數(shù)據(jù)較為完整,此時(shí)需要更新選票信息,為數(shù)據(jù)較為完整的節(jié)點(diǎn)投上一票。(說明之前的投票的節(jié)點(diǎn)數(shù)據(jù)不是集群中記錄最完整的,需要重新投一票)
以輪次為基礎(chǔ),為數(shù)據(jù)較為完整的節(jié)點(diǎn)投上一票后,阻塞隊(duì)列就又接收到了新一批選票,然后再次比較,依次循環(huán)下去
Leader選舉就是選出數(shù)據(jù)最完整的那臺(tái)節(jié)點(diǎn),因此選舉流程不會(huì)一直循環(huán)PK下去,會(huì)在流水記錄符合過半確認(rèn)時(shí)終止循環(huán)。而這個(gè)判斷條件是在為可能完整節(jié)點(diǎn)投票后,以服務(wù)ID為表示,記錄集群中為該服務(wù)節(jié)點(diǎn)投票信息,當(dāng)集群中過半節(jié)點(diǎn)都對(duì)該節(jié)點(diǎn)投了票時(shí)就認(rèn)定該節(jié)點(diǎn)有資格成為L(zhǎng)eader節(jié)點(diǎn),從而結(jié)束Leader選舉。
圖示如下
Zookeeper集群中選舉出Leader節(jié)點(diǎn)后,Leader節(jié)點(diǎn)會(huì)進(jìn)行LEADING狀態(tài)。Follower節(jié)點(diǎn)則會(huì)進(jìn)行FOLLOWING狀態(tài),開啟Leader確認(rèn)、同步流程。而observer節(jié)點(diǎn)則進(jìn)入OBSERVING狀態(tài),同樣準(zhǔn)備進(jìn)入同步流程。
預(yù)選Leader節(jié)點(diǎn)會(huì)先與Learner節(jié)點(diǎn)建立網(wǎng)絡(luò)連接,等待Follower、Observer節(jié)點(diǎn)接入。
cnxAcceptor = new LearnerCnxAcceptor(); // 這是一個(gè)連接線程 cnxAcceptor.start();
這個(gè)網(wǎng)絡(luò)連接十分重要,每個(gè)Leader會(huì)構(gòu)建一個(gè)LearnerCnxAcceptorHandler的處理器,核心過程如下
BufferedInputStream is = new BufferedInputStream(socket.getInputStream()); LearnerHandler fh = new LearnerHandler(socket, is, Leader.this); fh.start();
這個(gè)LearnerHandler處理所有Leader與learner的網(wǎng)絡(luò)IO交互
public class QuorumPacket implements Record { private int type; private long zxid; private byte[] data; private java.util.List<org.apache.zookeeper.data.Id> authinfo; }
Follower節(jié)點(diǎn)進(jìn)入FOLLOWING狀態(tài)后,首先會(huì)去查找Leader地址,然后建立網(wǎng)絡(luò)連接
QuorumServer leaderServer = findLeader(); connectToLeader(leaderServer.addr, leaderServer.hostname);
建立連接之后便發(fā)出type=FOLLOWERINFO的消息包,xid=ZxidUtils.makeZxid(self.getAcceptedEpoch(), 0),data=LearnerInfo
主要功能就是將當(dāng)前節(jié)點(diǎn)的epoch發(fā)送(所有連接節(jié)點(diǎn)都會(huì)發(fā)送INFO信息來給Leader提供集群中最大的epoch,以便確認(rèn)Leader的最新任期:lastAcceptedEpoch + 1,Leader節(jié)點(diǎn)會(huì)阻塞等待Follower節(jié)點(diǎn)接入直至滿足過半確認(rèn)時(shí)獲取最大的epoch)
long newEpochZxid = registerWithLeader(Leader.FOLLOWERINFO);
消息發(fā)出后等待Leader節(jié)點(diǎn)指示
Follower發(fā)出的FOLLOWERINFO消息包會(huì)在LearnerHandler被接收轉(zhuǎn)為:learnerInfoData,獲取Follower提供的zxid中的epoch
long lastAcceptedEpoch = ZxidUtils.getEpochFromZxid(qp.getZxid());
然后參與到新任Leader的epoch選舉中
long newEpoch = learnerMaster.getEpochToPropose(this.getSid(), lastAcceptedEpoch);
Leader接收到過半Follower提供的epoch后,發(fā)送最終epoch確認(rèn)消息包
QuorumPacket newEpochPacket = new QuorumPacket(Leader.LEADERINFO, newLeaderZxid, ver, null);
等待Follower確認(rèn)最終的epoch
learnerMaster.waitForEpochAck(this.getSid(), ss);
Follower之前阻塞在registerWithLeader中的INFO信息發(fā)送后,此時(shí)接收到Leader指示獲取Leader的epoch和zxid,發(fā)出確認(rèn)接收消息
QuorumPacket ackNewEpoch = new QuorumPacket(Leader.ACKEPOCH, lastLoggedZxid, epochBytes, null);
在接收到Leader給的zxid后,將自身狀態(tài)設(shè)為同步狀態(tài),開啟數(shù)據(jù)同步流程,等待Leader的同步數(shù)據(jù)。
self.setZabState(QuorumPeer.ZabState.SYNCHRONIZATION); syncWithLeader(newEpochZxid);
Leader接收到足夠的Follower的epoch確認(rèn)消息后,將自身狀態(tài)設(shè)為數(shù)據(jù)同步狀態(tài),與各個(gè)Learner開啟數(shù)據(jù)同步,阻塞等待Learner數(shù)據(jù)同步完成然后執(zhí)行自身的Leading
waitForNewLeaderAck(self.getId(), zk.getZxid());
數(shù)據(jù)同步發(fā)生在各個(gè)Leader建立的網(wǎng)絡(luò)連接中:LearnerHandler,在確認(rèn)到Learner接收了最新epoch消息后,立馬開啟數(shù)據(jù)同步流程
boolean needSnap = syncFollower(peerLastZxid, learnerMaster);
同步過程使用重入讀寫鎖:ReentrantReadWriteLock。并且獲取此Leader節(jié)點(diǎn)的lastProcessedZxid、minCommittedLog、maxCommittedLog
同步類型:DIFF:差異同步、TRUNC:回滾同步、SNAP: 全量同步,主要是TRUNC+DIFF,也就是learner上事務(wù)日志比leader上多的zxid進(jìn)行回滾,少的從leader接收進(jìn)行重做。
只有系統(tǒng)變量:zookeeper.forceSnapshotSync 配置了強(qiáng)制全量同步時(shí),會(huì)使用快照文件開啟全量同步。
到底是learner的事務(wù)日志多還是leader節(jié)點(diǎn)的事務(wù)日志多,是通過兩個(gè)節(jié)點(diǎn)的zxid比較的。事務(wù)日志是會(huì)落盤形成快照的,節(jié)點(diǎn)會(huì)緩存一定條數(shù)的事務(wù)日志,那部分具有minCommittedLog、maxCommittedLog的范圍屬性。在此范圍的數(shù)據(jù)可以直接發(fā)送,否則需要從磁盤讀取快照文件再次發(fā)送。
系統(tǒng)變量:zookeeper.snapshotSizeFactor 配置每次從磁盤讀取的快照數(shù)據(jù)所占快照總數(shù)的比例,默認(rèn)0.33即三分之一
系統(tǒng)變量:zookeeper.commitLogCount 配置節(jié)點(diǎn)緩存的事務(wù)日志數(shù)量,默認(rèn)500條
Follower節(jié)點(diǎn)阻塞在syncWithLeader中等待Leader同步的數(shù)據(jù),Leader同步數(shù)據(jù)主要就三種,DIFF、TRUNC、SNAP。其中DIFF是逐條的執(zhí)行提議,F(xiàn)ollower節(jié)點(diǎn)接收后會(huì)執(zhí)行,TRUNC接收到Follower則直接截?cái)嗳罩疚募街付ㄎ恢眉纯桑瑢?duì)于SNAP則清空dataTree根據(jù)傳輸?shù)娜罩疚募葱蛄屑纯?。處理完畢則發(fā)送確認(rèn)消息,正式對(duì)外提供服務(wù)。Leader節(jié)點(diǎn)接收到Follower的同步結(jié)束消息后,結(jié)束waitForNewLeaderAck,也會(huì)正式對(duì)外提供服務(wù)。
同步數(shù)據(jù)主流程如圖所示
數(shù)據(jù)同步細(xì)節(jié)如圖所示
Zookeeper支持客戶端對(duì)數(shù)據(jù)節(jié)點(diǎn)Znode注冊(cè)Watch監(jiān)聽,當(dāng)節(jié)點(diǎn)發(fā)生變化會(huì)及時(shí)反饋給該客戶端。Watch機(jī)制是Zookeeper分布式鎖、對(duì)外Leader選舉功能的基礎(chǔ)。
v3.6.0之前:watch是一次性注冊(cè),一旦反饋后會(huì)刪除,如果需要重復(fù)監(jiān)聽,則需要重復(fù)注冊(cè)
v3.6.0+ :支持配置監(jiān)聽模式:STANDARD(標(biāo)準(zhǔn)類型)、PERSISTENT(持久類型)、PERSISTENT_RECURSIVE(持久遞歸類型)。標(biāo)準(zhǔn)類型就是之前版本的一次性注冊(cè)類型;持久類型則是自動(dòng)重復(fù)注冊(cè)類型;持久遞歸類型則是持久類型的加強(qiáng)版本,不僅重復(fù)監(jiān)聽指定節(jié)點(diǎn),還重復(fù)監(jiān)聽節(jié)點(diǎn)的子節(jié)點(diǎn)。后兩種模式需要主動(dòng)移除Watch才能取消監(jiān)聽。
支持Watch的API:exist、getData、getChildren、addWatch
移除Watch的API:removeWatches
觸發(fā)Watch的API:setData、delete、create
只有注冊(cè)了Watch監(jiān)聽的客戶端才會(huì)收到反饋通知
客戶端收到的反饋通知順序與服務(wù)端節(jié)點(diǎn)處理順序一致
Watch依賴網(wǎng)絡(luò)連接的一次性通知,客戶端重連期間可能會(huì)導(dǎo)致Wacth丟失
標(biāo)準(zhǔn)Watch是一次性,多次觸發(fā)的Watch如果客戶端沒有來得及重置,會(huì)導(dǎo)致后續(xù)Watch通知丟失
標(biāo)準(zhǔn)Watch是一次性,持久Wach是服務(wù)端自動(dòng)注冊(cè)的,實(shí)質(zhì)還是一次性通知
持久Watch如果不主動(dòng)移除會(huì)一直通知下去
客戶端對(duì)指定節(jié)點(diǎn)注冊(cè)Watch
服務(wù)保存Watch,一旦觸發(fā)事件則發(fā)送消息通知
客戶端回調(diào)watch事件,進(jìn)一步處理
Watch監(jiān)聽注冊(cè)對(duì)象在Zookeeper中為WatchRegistration,不同API對(duì)應(yīng)的注冊(cè)對(duì)象不同
// getData、getConfig ---> DataWatchRegistration public class GetDataRequest implements Record { private String path; private boolean watch; } // exists ---> ExistsWatchRegistration public class ExistsRequest implements Record { private String path; private boolean watch; } // getChildren --> ChildWatchRegistration public class GetChildrenRequest implements Record { private String path; private boolean watch; } // @since 3.6.0 addWatch --> AddWatchRegistration public class AddWatchRequest implements Record { private String path; private int mode; }
這個(gè)請(qǐng)求又一個(gè)很明顯特點(diǎn):watch是一個(gè)布爾類型,也就是說只標(biāo)識(shí)是否watch
watch注冊(cè)
1.--> 客戶端:在發(fā)起API請(qǐng)求時(shí)構(gòu)建queuePacket對(duì)象。該對(duì)象主要包含RequestHeader、ReplyHeader、Record(Request)、Record(Response)、WatchRegistration等。其中RequestHeader標(biāo)記本次請(qǐng)求的操作類型 type 和事務(wù)zxid。服務(wù)端根據(jù)操作類型會(huì)有不同的解析流程。在queuePacket的構(gòu)造方法中會(huì)把queuePacket放入到一個(gè)阻塞隊(duì)列中outgoingQueue,該隊(duì)列會(huì)有專用的線程sendThread#clientCnxnSocket#doTransport從隊(duì)列中讀取元素并處理。如果是事務(wù)請(qǐng)求則把zxid++,然后序列化RequestHeader和Request,只把這兩者發(fā)送出去。queuePacket會(huì)放到pendingQueue中等待服務(wù)器返回,也就是說WatchRegistration并沒有發(fā)送出去,只是發(fā)送了操作標(biāo)識(shí)、zxi、path、watch標(biāo)志。
2.--> 服務(wù)端:在Zookeeper啟動(dòng)時(shí)會(huì)通過startServerCnxnFactory開啟服務(wù)端連接,此時(shí)會(huì)開啟AcceptThread來處理客戶端的連接,客戶端建立連接時(shí)會(huì)在這里處理,在接入時(shí)會(huì)構(gòu)建SelectorThread來通過handleIO處理客戶端的IO請(qǐng)求,然后將以此IO交互抽象為IOWorkRequest交給線程池去異步處理doWork-->doIO,然后開始解析網(wǎng)絡(luò)消息包readPayload,然后解析消息包processPacket,解析之后放到阻塞隊(duì)列submittedRequests中,隊(duì)列請(qǐng)求在RequestThrottler中處理,然后submitRequestNow交給ZkServer的處理鏈去鏈?zhǔn)教幚?。處理鏈初始化時(shí)指定了FinalRequestProcessor會(huì)處理與數(shù)據(jù)庫的交互。根據(jù)操作標(biāo)識(shí)type不同有不同的解析流程并反序列化數(shù)據(jù)構(gòu)建對(duì)應(yīng)的Request。如果Request中存儲(chǔ)watch標(biāo)識(shí)則在DataTree中通過IWatchManager添加記錄,記錄包含監(jiān)聽的路徑Path和客戶端的連接ServerCnxn等,這步就是服務(wù)端注冊(cè),這里IWatchManager區(qū)分為當(dāng)前節(jié)點(diǎn)和子節(jié)點(diǎn)2種。
private IWatchManager dataWatches; private IWatchManager childWatches;
將Request處理完畢就寫入Response中,構(gòu)建ReplyHeader、操作標(biāo)識(shí)、響應(yīng)狀態(tài)stat等返回了
3.--> 客戶端:在SendThread#readResponse中接收服務(wù)端的響應(yīng),此時(shí)阻塞隊(duì)列pendingQueue中取出之前outgoingQueue中保存的queuePacket對(duì)象,處理好響應(yīng)內(nèi)容就finishPacket,此時(shí)客戶端就完成Watch注冊(cè)和對(duì)應(yīng)API功能的響應(yīng)。finishPacket接收的是queuePacket對(duì)象,會(huì)處理其中的WatchRegistration,調(diào)用其register方法。register首先會(huì)拿到客戶端的ZKWatchManager中對(duì)應(yīng)類型的存儲(chǔ)列表。其中ZKWatchManager存儲(chǔ)以下列表
private final Map<String, Set<Watcher>> dataWatches = new HashMap<>(); private final Map<String, Set<Watcher>> existWatches = new HashMap<>(); private final Map<String, Set<Watcher>> childWatches = new HashMap<>(); private final Map<String, Set<Watcher>> persistentWatches = new HashMap<>(); private final Map<String, Set<Watcher>> persistentRecursiveWatches = new HashMap<>();
不同Map存儲(chǔ)不同類型的Watcher,register方法就是將WatchRegistration的實(shí)例存儲(chǔ)到HashMap中。這就是客戶端注冊(cè)。
圖示如下
Watch觸發(fā)
1.--> 客戶端:當(dāng)節(jié)點(diǎn)發(fā)送NodeCreated,NodeDeleted、NodeDataChanged、NodeChildrenChanged事件時(shí),會(huì)觸發(fā)Watch反饋,對(duì)應(yīng)API為:create、delete、setData。
2.--> 服務(wù)端:在以上API操作DataTree數(shù)據(jù)時(shí),會(huì)觸發(fā)IWatchManager#triggerWatch,不同API觸發(fā)的事件類型不同。triggerWatch會(huì)查詢相關(guān)Watch并調(diào)用其process放啊,當(dāng)然這是服務(wù)端的watch,也就是在服務(wù)端注冊(cè)的與操作節(jié)點(diǎn)Path相關(guān)的ServerCnxn。此時(shí)ServerCnxn會(huì)構(gòu)建一個(gè)notification類型客戶端響應(yīng)并發(fā)送給客戶端。如果WatchMode是默認(rèn)類型則觸發(fā)一次就移除了Watch標(biāo)識(shí),否則會(huì)持續(xù)觸發(fā)。
public static final WatcherMode DEFAULT_WATCHER_MODE = WatcherMode.STANDARD;
3.--> 客戶端:同樣在SendThread#readResponse中接收服務(wù)端的響應(yīng),解析是發(fā)現(xiàn)是notification類型響應(yīng),則構(gòu)建一個(gè)WatcherEvent,通過eventThread.queueEvent(we)構(gòu)建事件和對(duì)應(yīng)Watch列表的WatcherSetEventPair對(duì)象,然后放入阻塞隊(duì)列waitingEvents。構(gòu)建WatcherSetEventPair時(shí)會(huì)查找所有在客戶端ZKWatchManager中注冊(cè)的對(duì)指定事件感興趣的客戶端Watch。EventThread則不斷從waitingEvents取出事件,如果是WatcherSetEventPair則遍歷調(diào)用Watch的process方法,該方法就是客戶端需要重新的Watch監(jiān)聽處理方法。這樣就完成了整個(gè)Watch觸發(fā)機(jī)制
圖示如下
在V3.2.0+ 添加了 Chroot 特性,該特性允許每個(gè)客戶端為自己設(shè)置一個(gè)命名空間。這樣客戶端的所有操作都會(huì)在改命名空間下面,生成數(shù)據(jù)隔離。
Zookeeper支持對(duì)每個(gè)節(jié)點(diǎn)配置ACL權(quán)限,其中添加認(rèn)證用戶命令為:addauth scheme auth ,auth格式為:<username>:<passwprd>
ACL權(quán)限方案
方案 | 描述 | 設(shè)置命令 |
world | 只有一個(gè)用戶:anyone,代表所有人(默認(rèn)) | setAcl path world:anyobe:acl |
ip | 使用IP地址認(rèn)證 | setAcl path ip:<ip>:acl |
auth | 使用已添加認(rèn)證的用戶認(rèn)證 | setAcl path auth:<username>:acl |
digest | 使用“用戶名:密碼”方式認(rèn)證 | setAcl path digest:<username>:<passwprd>:acl |
ACL權(quán)限標(biāo)識(shí)
權(quán)限 | ACL簡(jiǎn)寫 | 描述 |
CREATE | c | 可以創(chuàng)建子節(jié)點(diǎn)(只能逐級(jí)創(chuàng)建,不能跨級(jí)別創(chuàng)建節(jié)點(diǎn)) |
DELETE | d | 可以刪除子節(jié)點(diǎn)(僅下一級(jí)節(jié)點(diǎn)) |
READ | r | 可以讀取節(jié)點(diǎn)數(shù)據(jù)及顯示子節(jié)點(diǎn)列表 |
WRITE | w | 可以設(shè)置節(jié)點(diǎn)數(shù)據(jù) |
ADMIN | a | 可以設(shè)置節(jié)點(diǎn)訪問控制列表權(quán)限 |
集群數(shù)量符合2N+1原則,當(dāng)集群宕機(jī)數(shù)量少于1/2時(shí),仍然能對(duì)外服務(wù)
v3.5.0+ 支持集群動(dòng)態(tài)配置,在主配置文件開啟屬性reconfigEnabled=true,配置屬性為:dynamicConfigFile= basePath/fileName ,動(dòng)態(tài)配置文件名格式為:configFilename.dynamic[.version]。 內(nèi)容只支持server、group 、 weight屬性,其中server配置格式為
server.<positive id> = <address1>:<port1>:<port2>[:role];[<client port address>:]<client port>
角色role可選值為:participant(參與者,運(yùn)行中為follower,默認(rèn)值)、observer(觀察者)。修改后需要調(diào)用客戶端命令reconfig
v3.6.0+ 支持度量指標(biāo)監(jiān)控,可配合Prometheus.io通過Jetty服務(wù)器對(duì)外輸出。訪問地址為:http://hostname:httPort/metrics。此時(shí)可以使用grafana進(jìn)行可視化監(jiān)控。
感謝各位的閱讀,以上就是“分布式協(xié)調(diào)服務(wù)組件Zookeeper的必備知識(shí)點(diǎn)有哪些”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)分布式協(xié)調(diào)服務(wù)組件Zookeeper的必備知識(shí)點(diǎn)有哪些這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
分享標(biāo)題:分布式協(xié)調(diào)服務(wù)組件Zookeeper的必備知識(shí)點(diǎn)有哪些
文章網(wǎng)址:http://jinyejixie.com/article44/gdphee.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、營(yíng)銷型網(wǎng)站建設(shè)、面包屑導(dǎo)航、、域名注冊(cè)、軟件開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)