3.ZooKeeper集群啟動(dòng)過程

基礎(chǔ)

配置文件
  1. zk的配置文件中可以配置三個(gè)端口
    1. clientPort=2181 這個(gè)是配置服務(wù)端用來接收客戶端連接的端口纽什。
    2. server.1=127.0.0.1:2888:3888
      1. 配置服務(wù)器1的ip地址
      2. 2888端口為集群peer之間的通信端口,除了選舉以外都用這個(gè)端口
      3. 3888端口為選舉端口蜡吧,只有選舉的時(shí)候使用。

1. 數(shù)據(jù)加載階段

  1. 集群版的入口在QuorumPeerMain.main():
    1. 讀取并解析配置文件雅镊。
    2. 構(gòu)建并啟動(dòng)對(duì)事務(wù)日志文件和快照文件的定期刪除清理任務(wù)。清理策略一般是保留最近的3個(gè)文件。
    3. 集群版會(huì)調(diào)用runFromConfig
  2. runFromConfig():
    1. 創(chuàng)建QuorumPeer并賦值锯茄,QuorumPeer extends ZooKeeperThread,是一個(gè)線程任務(wù)
    2. QuorumPeer復(fù)寫了Thread.start()方法莉御,方法內(nèi)部邏輯為:
      1. 加載磁盤快照和提交日志撇吞,生成或者說是恢復(fù)內(nèi)存DataTree
      2. 初始化并啟動(dòng)接收client端連接的線程,監(jiān)聽clientPort礁叔。處理連接默認(rèn)使用NIOServerCnxnFactory(內(nèi)部使用jdk原生nio)牍颈,也可以選擇使用NettyServerCnxnFactory(netty版)。
      3. 這里提前說下琅关,對(duì)于client連接煮岁,zk使用的是nio,對(duì)于集群peer之間內(nèi)部連接涣易,zk使用的jdk bio画机。這個(gè)用的很好,很適合各自的情況新症,可以想下步氏。
      4. 啟動(dòng)jetty web服務(wù)器
      5. 開始leader選舉
      6. 在main主線程調(diào)用quorumPeer.join(),等待quorumPeer執(zhí)行完畢徒爹。

2. Leader選舉開始

  1. 初始服務(wù)器狀態(tài)為LOOKING荚醒,此時(shí)每個(gè)peer都會(huì)創(chuàng)建投票,寫入自己的sid隆嗅,最大zxid界阁,currentEpoch,表示選自己做leader胖喳,投給自己泡躯。
  2. 根據(jù)選舉類型創(chuàng)建選舉算法,以前的都過時(shí)了丽焊,現(xiàn)在就剩下FastLeaderElection這個(gè)算法了较剃。
  3. 創(chuàng)建新的QuorumCnxManager,替換舊的qcmRef粹懒,將新的qcm傳入FastLeaderElection線程重付,啟動(dòng)線程,開始選舉凫乖。
    1. 先說下QuorumCnxManager:
      1. 根據(jù)配置的集群地址和選舉端口确垫,每個(gè)peer都會(huì)主動(dòng)去連接及群眾的其他服務(wù)器,然后根據(jù)sid判斷帽芽,只保留sid大的連接小的這個(gè)方向的連接删掀,銷毀掉sid小的連接大的這個(gè)方向的連接。這樣每個(gè)peer都有且只有一個(gè)與其他服務(wù)器的連接导街,該鏈接為長連接披泪,使用jdk bio實(shí)現(xiàn)。
      2. QuorumCnxManager就是選舉連接管理器搬瑰,將收到的投票數(shù)據(jù)放到QuorumCnxManager.recvQueue中款票。
      3. 而選舉過后正常服務(wù)時(shí)控硼,使用的是服務(wù)端口,是所有follower主動(dòng)連接leader艾少,只保留這個(gè)方向的連接卡乾,不管sid大小嫩舟。
  4. FastLeaderElection線程的算法邏輯
    1. 每個(gè)peer都會(huì)把自己的投票轉(zhuǎn)發(fā)給集群中的其他服務(wù)器筷弦,放到sendqueue中,有獨(dú)立的發(fā)送線程發(fā)送傍菇。
    2. 獨(dú)立的接收線程會(huì)不斷的從QuorumCnxManager.recvQueue中拿數(shù)據(jù)谍椅,解析误堡,轉(zhuǎn)為投票的結(jié)構(gòu)體,放到FastLeaderElection.recvqueue中雏吭。
    3. 只要當(dāng)前peer的狀態(tài)時(shí)LOOKING锁施,就會(huì)不斷的從FastLeaderElection.recvqueue中取投票數(shù)據(jù),先判斷是否有效思恐,然后查看遠(yuǎn)程服務(wù)器的狀態(tài)沾谜,如果也是LOOKING,則:
      1. 將遠(yuǎn)程服務(wù)器的投票數(shù)據(jù)和當(dāng)前服務(wù)器的投票數(shù)據(jù)進(jìn)行對(duì)比胀莹,返回是否應(yīng)該采用遠(yuǎn)程的投票(即當(dāng)前服務(wù)器投票的服務(wù)器數(shù)據(jù)較老)
        1. 遠(yuǎn)程服務(wù)器的epoch更大基跑,則使用遠(yuǎn)程的投票。
        2. epoch一樣大描焰,遠(yuǎn)程的zxid更大媳否,則使用遠(yuǎn)程的投票。
        3. epoch荆秦、zxid一樣大篱竭,遠(yuǎn)程的serverid更大,則使用遠(yuǎn)程的投票步绸〔舯疲總之一定會(huì)比較出來到底用哪個(gè)投票的服務(wù)器,不會(huì)有模棱兩可的結(jié)果瓤介。
      2. 如果使用遠(yuǎn)程服務(wù)器的投票吕喘,則清空投票箱,重新投刑桑。
    4. 如果遠(yuǎn)程服務(wù)器的狀態(tài)是FOLLOWING或者LEADING:
      1. 可以確定遠(yuǎn)程服務(wù)器已經(jīng)知道了leader是誰氯质,它投過來的票記錄的就是leader,我們使用這個(gè)投票的leader就可以了祠斧。
    5. 投票結(jié)束闻察,會(huì)調(diào)用QuorumPeer.setPeerState更新當(dāng)前peer的狀態(tài)。

3. Leader選舉結(jié)束后

Leader選舉結(jié)束后的邏輯主要集中在Leader,LearnerHandler,Follower,Learner這幾個(gè)類中。

  1. 選舉結(jié)束后辕漂,F(xiàn)astLeaderElection算法邏輯中呢灶,會(huì)調(diào)用QuorumPeer.setPeerState(...)方法,將當(dāng)前peer的最新角色通過synchronized同步鎖钉嘹,通知給QuorumPeer.run()方法里的while循環(huán)填抬,這樣當(dāng)前peer就知道了自己的角色,走新的角色邏輯隧期。
  2. 假如當(dāng)前peer是Leader
    1. leader首先會(huì)啟動(dòng)一個(gè)獨(dú)立的LearnerCnxAcceptor線程,這個(gè)線程主要邏輯就是赘娄,使用bio的ServerSocket仆潮,監(jiān)聽通訊端口,accept每個(gè)Follower主動(dòng)發(fā)來的連接遣臼,為每一個(gè)連接創(chuàng)建一個(gè)LearnerHandler線程性置,將socket交給該線程處理。
    2. 轉(zhuǎn)3.1
    3. leader會(huì)阻塞等待所有Follower發(fā)來消息揍堰,知道確認(rèn)最新的epoch是多少
    4. 然后將最新的epoch發(fā)給所有follower鹏浅,阻塞等待Follower對(duì)最新epoch的ack。
    5. 轉(zhuǎn)3.4
    6. leader在收到Follower對(duì)epoch的ack包后屏歹,判斷超過半數(shù)都接受隐砸,那么該epoch就可用。
    7. 根據(jù)這次Follower發(fā)來的消息蝙眶,leader與自己的數(shù)據(jù)做比較季希,
      1. 如果發(fā)現(xiàn)某個(gè)Follower的最大zxid與自己相同,就發(fā)DIFF包幽纷,表示不需要同步式塌。
      2. 其他情況發(fā)不同的包,如TRUNC友浸,SNAP等峰尝,指明Follower與leader如何同步數(shù)據(jù)。
    8. 轉(zhuǎn)3.7
    9. 不會(huì)有對(duì)同步數(shù)據(jù)的ack收恢,直接再發(fā)一個(gè)NEWLEADER包給follower武学,包括了sid,zxid派诬,仲裁器等信息劳淆。然后阻塞等待follower對(duì)NEWLEADER包的ack響應(yīng)達(dá)到大多數(shù)。轉(zhuǎn)3.8
    10. 大多數(shù)follower響應(yīng)后默赂,leader會(huì)發(fā)送UPTODATE包沛鸵,表示數(shù)據(jù)已經(jīng)都同步好了,集群服務(wù)的準(zhǔn)備工作都完成了,可以對(duì)client提供服務(wù)了曲掰。轉(zhuǎn)3.9.
  3. 假如當(dāng)前peer是Learner中的Follower:
    1. Follower會(huì)根據(jù)選舉時(shí)的投票和配置信息疾捍,重新獲取leader的地址和通信端口,主動(dòng)連接leader栏妖,建立與leader之間的長連接乱豆。
    2. 發(fā)送第一個(gè)包,是將自己的sid吊趾,最大zxid等信息發(fā)給leader宛裕。
    3. 轉(zhuǎn)上面步驟2.3,2.4
    4. Follower在收到leader發(fā)來的最新的epoch后论泛,與自己當(dāng)前的epoch比較揩尸,
      1. 如果leader的epoch最大,就用這個(gè)epoch作為ack響應(yīng)的epoch屁奏。
      2. 如果自己用的比leader的大岩榆,表示有問題,用-1作為ack響應(yīng)的epoch坟瓢。
    5. Follower會(huì)將sid勇边,使用的epoch等信息作為epoch的ack包發(fā)給leader。
    6. 轉(zhuǎn)2.6
    7. follower收到同步方式和需要同步的數(shù)據(jù)后折联,會(huì)完成數(shù)據(jù)同步粒褒,完成同步后,不會(huì)有對(duì)同步結(jié)果的ack包發(fā)給leader诚镰,就是接著等著讀取leader的數(shù)據(jù)怀浆,轉(zhuǎn)2.9
    8. 收到leader發(fā)來的NEWLEADER包,使用包里的仲裁器和epoch等數(shù)據(jù)作為自己的數(shù)據(jù)怕享。發(fā)送ack包給leader执赡。轉(zhuǎn)2.10
    9. 因?yàn)樵谕綌?shù)據(jù)階段,會(huì)用while循環(huán)讀取leader發(fā)來的數(shù)據(jù)函筋,使用的bio沙合,無消息的時(shí)候,流本身就有阻塞的作用跌帐。正是使用此處流的阻塞首懈,來保證在learner啟動(dòng)初始化階段,只要沒有收到leader發(fā)來的UPTODATE包谨敛,就一直循環(huán)讀取或者阻塞等待究履,不會(huì)對(duì)外提供服務(wù),處理client的請(qǐng)求脸狸。收到UPTODATE包后跳出while循環(huán)最仑,解除阻塞藐俺。進(jìn)入讀取和處理client數(shù)據(jù)的while循環(huán)中。

4. 正式對(duì)client提供服務(wù)

  1. leader:
    1. leader線程會(huì)不斷的循環(huán)泥彤,檢測(cè)每個(gè)LearnerHandler線程是否存活欲芹,定時(shí)的ping每一個(gè)learner,判斷與Learner的ping是否超時(shí)等吟吝,以此來判斷某個(gè)learner是否存活菱父,再根據(jù)這個(gè)判斷是否還能夠達(dá)成多數(shù)follower的有效仲裁,如果不能有效仲裁了剑逃,就退出循環(huán)浙宜,重新選舉。
  2. follower:
    1. follower
  3. 處理器都是繼承自Thred蛹磺,本身是一個(gè)線程梆奈,說說通用的處理器流程:
    1. CommitProcessor:
      1. 對(duì)服務(wù)器來說,只會(huì)啟動(dòng)一個(gè)CommitProcessor線程称开。
      2. 在線程第一次運(yùn)行時(shí),因?yàn)閝ueuedRequests和committedRequests都為空乓梨,所以線程阻塞wait鳖轰。
      3. 然后上一個(gè)處理器調(diào)用CommitProcessor.processRequest()提交請(qǐng)求到queuedRequests隊(duì)列中。
      4. 因?yàn)榇藭r(shí)是第一個(gè)請(qǐng)求扶镀,無待commit請(qǐng)求蕴侣,所以會(huì)調(diào)用notifyAll喚醒阻塞的所有線程。
      5. 線程喚醒后臭觉,因?yàn)閝ueuedRequests不為空昆雀,所以跳出while循環(huán),向下走蝠筑。此時(shí)下一個(gè)while循環(huán)為true狞膘,因?yàn)闆]有待commit或者正在commit的請(qǐng)求,而且從queuedRequests拉數(shù)據(jù)不為空什乙。
      6. 判斷這個(gè)請(qǐng)求是不是需要被commit的請(qǐng)求挽封,即寫請(qǐng)求。
        1. 如果是讀請(qǐng)求臣镣,更新正在處理請(qǐng)求的計(jì)數(shù)numRequestsProcessing+1辅愿,根據(jù)請(qǐng)求的sessionId對(duì)線程數(shù)組取余,拿到執(zhí)行線程忆某,封裝成任務(wù)点待,提交給該線程,線程內(nèi)操作就是將該讀請(qǐng)求提交給下一個(gè)執(zhí)行器弃舒。即調(diào)用nextProcessor.processRequest(request);一般就是走到FinalRequestProcessor癞埠,會(huì)根據(jù)路徑取對(duì)應(yīng)內(nèi)存DataTree的node節(jié)點(diǎn),將數(shù)據(jù)封裝成響應(yīng),寫回給客戶端燕差≡馑瘢回到CommitProcessor后會(huì)執(zhí)行finally塊,里面會(huì)將currentlyCommitting置為null徒探,將正在處理請(qǐng)求的計(jì)數(shù)numRequestsProcessing-1瓦呼。相當(dāng)于該numRequestsProcessing最大值為1。
        2. 如果是寫請(qǐng)求测暗,則set為待commit請(qǐng)求央串,賦值給nextPending變量。因?yàn)橛辛舜齝ommit請(qǐng)求碗啄,所以再走while循環(huán)條件不成立质和,跳出while,向下走稚字。
      7. 因?yàn)閏ommittedRequests為空饲宿,所以if條件不成立,走回while循環(huán)胆描,繼續(xù)阻塞wait瘫想。
      8. 假如后來再有一個(gè)寫請(qǐng)求入隊(duì)queuedRequests。根據(jù)上面說的邏輯昌讲,會(huì)賦值給nextPending變量国夜。
      9. 假如leader對(duì)寫的請(qǐng)求投票結(jié)束,確定要commit該數(shù)據(jù)短绸,會(huì)給Follower發(fā)送COMMIT,給Observer發(fā)送INFORM命令车吹。處理器鏈會(huì)調(diào)用CommitProcessor.commit,將待commit請(qǐng)求入隊(duì)committedRequests醋闭。調(diào)用notifyAll喚醒阻塞線程窄驹。
      10. 此時(shí)因?yàn)閏ommittedRequests不為空,并且也沒有正在commit的請(qǐng)求证逻,所以跳出while馒吴,向下走。
      11. 從committedRequests出隊(duì)該已確定要被提交的請(qǐng)求瑟曲,set為正在commit的請(qǐng)求饮戳,即賦值給currentlyCommitting。然后將待commit變量置為null洞拨,即nextPending為null扯罐。
      12. 更新正在處理請(qǐng)求的計(jì)數(shù)numRequestsProcessing+1,根據(jù)請(qǐng)求的sessionId對(duì)線程數(shù)組取余烦衣,拿到執(zhí)行線程歹河,封裝成任務(wù)掩浙,提交給該線程,線程內(nèi)操作就是將該讀請(qǐng)求提交給下一個(gè)執(zhí)行器秸歧。即調(diào)用nextProcessor.processRequest(request);一般就是走到FinalRequestProcessor厨姚。FinalRequestProcessor會(huì)將寫請(qǐng)求的數(shù)據(jù),寫到內(nèi)存DataTree上键菱,寫成功響應(yīng)給client谬墙。
      13. 回到CommitProcessor后會(huì)執(zhí)行finally塊,里面會(huì)將currentlyCommitting置為null经备,將正在處理請(qǐng)求的計(jì)數(shù)numRequestsProcessing-1拭抬。相當(dāng)于該numRequestsProcessing最大值為1。
      14. 假如在有一個(gè)寫請(qǐng)求處于待commit狀態(tài)侵蒙,且CommitProcessor線程處于wait狀態(tài)造虎,此時(shí)有一個(gè)讀請(qǐng)求或者寫請(qǐng)求被上一個(gè)處理器塞到了queuedRequests中,因?yàn)镃ommitProcessor線程阻塞等待leader的提交命令來喚醒纷闺,所以在沒有l(wèi)eader的commit命令之前算凿,都是阻塞的,不會(huì)從queuedRequests隊(duì)列中去拿后面的讀或者寫請(qǐng)求犁功。相當(dāng)于沒有寫的情況下氓轰,可以同時(shí)有多個(gè)讀,只要有寫的情況下波桩,只能有一個(gè)寫。很類似于讀寫鎖请敦。
1. **FinalRequestProcessor:**
    1. 對(duì)服務(wù)器來說镐躲,只會(huì)啟動(dòng)一個(gè)FinalRequestProcessor線程。
    2. 根據(jù)request.hdr字段來判斷侍筛,如果有值證明是寫請(qǐng)求萤皂,如果為null,證明是讀請(qǐng)求匣椰。如果是寫請(qǐng)求裆熙,將結(jié)果應(yīng)用在內(nèi)存DataTree上,觸發(fā)事件通知禽笑,返回執(zhí)行結(jié)果入录。
    3. 如果是寫請(qǐng)求,還會(huì)刪除對(duì)應(yīng)的outstandingChanges數(shù)據(jù)佳镜,將請(qǐng)求放到committedLog已提交列表中僚稿,方便與follower快速同步數(shù)據(jù)。
    4. 獲取請(qǐng)求中的客戶端信息蟀伸,如果為null蚀同,證明請(qǐng)求是leader發(fā)來的commit命令等缅刽,不是直連的客戶端發(fā)給自己的寫請(qǐng)求,不需要繼續(xù)執(zhí)行蠢络。
    5. 如果客戶端信息不為null衰猛,證明是客戶端直連該follower發(fā)的寫請(qǐng)求,根據(jù)request.type判斷是什么命令刹孔,構(gòu)造響應(yīng)啡省,寫回給客戶端。
        1. 比如是getData請(qǐng)求芦疏,那么會(huì)取路徑對(duì)應(yīng)的Node數(shù)據(jù)冕杠,檢查用戶的讀權(quán)限,將數(shù)據(jù)和狀態(tài)封裝成響應(yīng)寫回給請(qǐng)求方酸茴。
        2. 如果是create請(qǐng)求分预,因?yàn)樯厦嬉呀?jīng)更新過內(nèi)存DataTree的數(shù)據(jù)了,此時(shí)只需要返回路徑和狀態(tài)給請(qǐng)求方就可以了薪捍。

2. **SyncRequestProcessor:**
    1. 對(duì)服務(wù)器來說笼痹,只會(huì)啟動(dòng)一個(gè)SyncRequestProcessor線程。
    2. SyncRequestProcessor在3種不同情況下使用: 
        1. Leader - 將請(qǐng)求同步到磁盤酪穿,并將其轉(zhuǎn)發(fā)到AckRequestProcessor凳干,后者將ack發(fā)回給自己。 
        2. Follower - 將請(qǐng)求同步到磁盤被济,并將請(qǐng)求轉(zhuǎn)發(fā)到SendAckRequestProcessor救赐,后者將數(shù)據(jù)包發(fā)送到leader。 SendAckRequestProcessor是可刷新的只磷,這使我們能夠?qū)⑼扑蛿?shù)據(jù)包強(qiáng)制發(fā)送到leader经磅。 
        3. Observer - 將提交的請(qǐng)求同步到磁盤(作為INFORM數(shù)據(jù)包接收)。 它永遠(yuǎn)不會(huì)將確認(rèn)發(fā)送回給leader钮追,因此nextProcessor將為null预厌。 因?yàn)樗话峤坏膖xns,所以這改變了觀察者上txnlog的語義元媚。
    3. 該處理器的執(zhí)行時(shí)機(jī)和主要作用: 
        1. 對(duì)leader來說: 
            1. 對(duì)于一個(gè)寫操作轧叽,先經(jīng)過預(yù)處理器,封裝好要寫的數(shù)據(jù)刊棕,然后提交給下一個(gè)處理器提案處理器 
            2. 提案處理器將要寫的數(shù)據(jù)封裝到提案請(qǐng)求中炭晒,發(fā)給所有follower
            3. 然后執(zhí)行當(dāng)前同步處理器,將這個(gè)寫請(qǐng)求寫到事務(wù)日志磁盤緩沖區(qū)甥角,緩沖區(qū)對(duì)象LinkedList toFlush腰埂,大小一般為1000,超過該值會(huì)執(zhí)行flush磁盤操作蜈膨。 
        2. 對(duì)follower來說: 
            1. 接到leader發(fā)來的提案請(qǐng)求后屿笼,follower基本就是先執(zhí)行該處理器牺荠,即將這個(gè)寫請(qǐng)求寫到事務(wù)日志磁盤緩沖區(qū)。 
        3. 對(duì)leader和follower來說驴一,在寫完一定數(shù)量的寫請(qǐng)求后休雌,會(huì)隨機(jī)觸發(fā)拍快照操作。
        4. 只要能寫完事務(wù)緩存肝断,沒有異常中斷杈曲,就表示贊同提案,調(diào)下一個(gè)處理器發(fā)送應(yīng)答給leader胸懈。 相當(dāng)于只要發(fā)了應(yīng)答担扑,就表示贊同提案,出現(xiàn)異橙で或者錯(cuò)誤涌献、網(wǎng)絡(luò)問題等導(dǎo)致沒有發(fā)應(yīng)答給leader,就表示不贊同首有。 沒有中間態(tài)度燕垃,而且只要能將事務(wù)寫到磁盤,就一定要投贊同票井联。
  1. 處理器都是繼承自Thred卜壕,不同角色服務(wù)器的處理器鏈:
    1. Observer:
      1. ObserverRequestProcessor
        1. client.getData請(qǐng)求到ObserverRequestProcessor,入隊(duì)再從隊(duì)列取出烙常,調(diào)用nextProcessor.processRequest轴捎,交給下一個(gè)處理器,即CommitProcessor處理蚕脏。
        2. 如果是寫請(qǐng)求侦副,將請(qǐng)求放到下一個(gè)處理器的隊(duì)列后,會(huì)轉(zhuǎn)發(fā)該寫請(qǐng)求給leader蝗锥。
      2. CommitProcessor
      3. FinalRequestProcessor
    2. Follower:
      1. FollowerRequestProcessor
        1. 和ObserverRequestProcessor的邏輯差不多跃洛。
      2. CommitProcessor
      3. FinalRequestProcessor
      4. 還有獨(dú)立的SyncRequestProcessor -> SendAckRequestProcessor
      5. SendAckRequestProcessor:
        1. 向leader發(fā)送ACK率触。
    3. Leader:
      1. LeaderRequestProcessor
        1. 負(fù)責(zé)執(zhí)行本地會(huì)話升級(jí)终议。 只有直接提交給領(lǐng)導(dǎo)者的請(qǐng)求才能通過此處理器。即客戶端直連的請(qǐng)求才會(huì)走這個(gè)處理器葱蝗。
      2. PrepRequestProcessor
        1. 首先是單線程穴张,不斷從LinkedBlockingQueue中拿請(qǐng)求數(shù)據(jù),根據(jù)request.type判斷屬于客戶端的哪一種請(qǐng)求两曼。
        2. 如果是新增節(jié)點(diǎn)皂甘、修改節(jié)點(diǎn)等寫操作,會(huì)調(diào)用zks.getNextZxid()悼凑,拿到遞增的zxid(事務(wù)id)偿枕。
        3. 然后根據(jù)內(nèi)存樹DataTree的現(xiàn)有數(shù)據(jù)璧瞬,來計(jì)算新的數(shù)據(jù)path名稱,版本號(hào)渐夸,acl都應(yīng)該是什么嗤锉,封裝數(shù)據(jù),沿著處理器鏈向下傳遞墓塌。
        4. 注意這里的操作不會(huì)對(duì)內(nèi)存樹有影響瘟忱,也就是操作結(jié)果不會(huì)在內(nèi)存樹生效。只是得到數(shù)據(jù)應(yīng)該是什么樣苫幢,比如創(chuàng)建的節(jié)點(diǎn)名稱是什么等等访诱。
      3. ProposalRequestProcessor(包含獨(dú)立運(yùn)行的線程SyncRequestProcessor->AckRequestProcessor)
        1. 這個(gè)處理器將請(qǐng)求分成兩種進(jìn)行處理,
          1. Follower轉(zhuǎn)發(fā)過來的"sync"同步請(qǐng)求韩肝。
          2. 剩下的其他請(qǐng)求,主要是其他learner server端轉(zhuǎn)過來的寫請(qǐng)求触菜,以及client的讀請(qǐng)求。
        2. 先說如何處理Follower轉(zhuǎn)發(fā)過來的"sync"同步請(qǐng)求:
          1. zk不能保證每個(gè)服務(wù)實(shí)例在每個(gè)時(shí)間都具有相同的ZooKeeper數(shù)據(jù)視圖伞梯。由于網(wǎng)絡(luò)延遲之類的因素玫氢, 一個(gè)客戶端可能會(huì)在另一客戶端收到更改通知之前執(zhí)行更新∶战耄考慮兩個(gè)客戶端A和B的情況漾峡, 如果客戶端A將znode/a的值從0設(shè)置為1,然后告訴客戶端B讀取/a喻旷,則客戶端B可能讀取舊值0生逸,具體取決于連的哪個(gè)服務(wù)器。 如果客戶端A和客戶端B讀取相同的值很重要且预,則客戶端B應(yīng)該在執(zhí)行讀取之前從ZooKeeper API方法中調(diào)用sync()方法槽袄。 sync是使得client當(dāng)前連接著的ZooKeeper服務(wù)器,和ZooKeeper的Leader節(jié)點(diǎn)同步(sync)一下數(shù)據(jù)锋谐。 用法一般是同一線程串行執(zhí)行遍尺,先調(diào) zookeeper.sync("關(guān)注的路徑path","可阻塞的回調(diào)對(duì)象","回調(diào)上下文對(duì)象"), 調(diào)用完后涮拗,會(huì)再調(diào)用"可阻塞的回調(diào)對(duì)象.await等阻塞方法"乾戏,等待Leader和當(dāng)前Follower數(shù)據(jù)同步完成,返回響應(yīng)三热, 然后就可以調(diào)用zookeeper.getData("關(guān)注的路徑path")了鼓择,可以保證在該路徑的數(shù)據(jù)上,獲取到和Leader一致的視圖就漾。 具體可以參考o(jì)rg.apache.zookeeper.cli.SyncCommand#exec()和org.apache.zookeeper.server.quorum .EphemeralNodeDeletionTest#testEphemeralNodeDeletion()呐能。
          2. 當(dāng)follower收到到客戶端發(fā)來的sync請(qǐng)求時(shí),會(huì)將這個(gè)請(qǐng)求添加到一個(gè)pendingSyncs隊(duì)列里抑堡,然后將這個(gè)請(qǐng)求發(fā)送給leader摆出, 直到收到leader的Leader.SYNC響應(yīng)消息時(shí)朗徊,才將這個(gè)請(qǐng)求從pendingSyncs隊(duì)列里移除,并commit這個(gè)請(qǐng)求偎漫。
          3. 當(dāng)Leader收到一個(gè)sync請(qǐng)求時(shí)荣倾,如果leader當(dāng)前沒有待commit的決議,那么leader會(huì)立即發(fā)送一個(gè)Leader.SYNC消息給follower骑丸。 否則舌仍,leader會(huì)等到當(dāng)前最后一個(gè)待commit的決議完成后,再發(fā)送Leader.SYNC消息給Follower通危。
          4. 其實(shí)這里面有一個(gè)隱含的邏輯铸豁,leader和follower之間的消息通信,是嚴(yán)格按順序來發(fā)送的(TCP保證)菊碟, 因此节芥,當(dāng)follower接收到Leader.SYNC消息時(shí), 說明follower也一定接收到了leader之前(在leader接收到sync請(qǐng)求之前)發(fā)送的所有提案或者commit消息逆害。 這樣就可以確保follower內(nèi)存中的數(shù)據(jù)和leader是同步的了头镊。客戶端就能從連接的follower讀取到最新的數(shù)據(jù)了魄幕。
        3. 剩下的其他請(qǐng)求相艇,主要是其他learner server端轉(zhuǎn)過來的寫請(qǐng)求,以及client的讀請(qǐng)求纯陨。 交個(gè)下一個(gè)處理器執(zhí)行坛芽,即CommitProcessor。
        4. AckRequestProcessor:它只是將前一個(gè)處理器的請(qǐng)求作為ACK轉(zhuǎn)發(fā)給leader翼抠。相當(dāng)于對(duì)于寫數(shù)據(jù)咙轩,leader自己給自己投贊同票。
      4. CommitProcessor
      5. Leader.ToBeAppliedRequestProcessor
        1. 該請(qǐng)求處理器僅維護(hù)toBeApplied列表阴颖。將已確定提交活喊,待應(yīng)用到內(nèi)存樹的請(qǐng)求轉(zhuǎn)發(fā)給下一個(gè)處理器執(zhí)行。
      6. FinalRequestProcessor
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末量愧,一起剝皮案震驚了整個(gè)濱河市钾菊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌侠畔,老刑警劉巖结缚,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件损晤,死亡現(xiàn)場(chǎng)離奇詭異软棺,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)尤勋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門喘落,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茵宪,“玉大人,你說我怎么就攤上這事瘦棋∠』穑” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵赌朋,是天一觀的道長凰狞。 經(jīng)常有香客問我,道長沛慢,這世上最難降的妖魔是什么赡若? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮团甲,結(jié)果婚禮上逾冬,老公的妹妹穿的比我還像新娘。我一直安慰自己躺苦,他們只是感情好身腻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著匹厘,像睡著了一般嘀趟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上愈诚,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天去件,我揣著相機(jī)與錄音,去河邊找鬼扰路。 笑死尤溜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的汗唱。 我是一名探鬼主播宫莱,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼哩罪!你這毒婦竟也來了授霸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤际插,失蹤者是張志新(化名)和其女友劉穎碘耳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體框弛,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辛辨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斗搞。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡指攒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出僻焚,到底是詐尸還是另有隱情允悦,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布虑啤,位于F島的核電站隙弛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏狞山。R本人自食惡果不足惜驶鹉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望铣墨。 院中可真熱鬧室埋,春花似錦、人聲如沸伊约。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屡律。三九已至腌逢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間超埋,已是汗流浹背搏讶。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留霍殴,地道東北人媒惕。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像来庭,于是被迫代替她去往敵國和親妒蔚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 集群和單機(jī)版啟動(dòng)類都是QuorumPeerMain月弛,進(jìn)入initializeAndRun方法 啟動(dòng) 解析配置文件z...
    Kohler閱讀 283評(píng)論 0 0
  • 集群處理請(qǐng)求分兩種:事務(wù)和非事務(wù)肴盏,對(duì)于非事務(wù),請(qǐng)求處理和單機(jī)類似帽衙,節(jié)點(diǎn)本地就可以完成數(shù)據(jù)的請(qǐng)求菜皂;事務(wù)請(qǐng)求需要提交給...
    Kohler閱讀 987評(píng)論 0 2
  • 集群版服務(wù)器啟動(dòng) 啟動(dòng)類是org.apache.zookeeper.server.quorum.QuorumPee...
    tracy_668閱讀 1,007評(píng)論 0 2
  • 加載解析配置文件,明確磁盤快照路徑厉萝、事務(wù)日志路徑恍飘、心跳時(shí)間榨崩、serverid、集群中的其他服務(wù)器地址和端口常侣、角色 ...
    whslowly閱讀 373評(píng)論 0 0
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友弹渔。感恩相遇胳施!感恩不離不棄。 中午開了第一次的黨會(huì)肢专,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,567評(píng)論 0 11