<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ZooKeeper系列文章:ZooKeeper 源碼和實(shí)踐揭秘(三)

          共 10530字,需瀏覽 22分鐘

           ·

          2021-12-18 03:05


          導(dǎo)語


          ZooKeeper 是個(gè)針對大型分布式系統(tǒng)的高可用、高性能且具有一致性的開源協(xié)調(diào)服務(wù),被廣泛的使用。對于開發(fā)人員,ZooKeeper 是一個(gè)學(xué)習(xí)和實(shí)踐分布式組件的不錯(cuò)的選擇。本文對 ZooKeeper 的源碼進(jìn)行簡析,也會介紹 ZooKeeper 實(shí)踐經(jīng)驗(yàn),希望能幫助到 ZooKeeper 初學(xué)者?。文章部分內(nèi)容參考了一些網(wǎng)絡(luò)文章,已標(biāo)注在末尾參考文獻(xiàn)中。



          ZooKeeper簡介


          1. 初衷


          在業(yè)務(wù)中使用了 ZooKeeper 作為消息系統(tǒng),在開發(fā)和運(yùn)維過程中,也遇到一些問題,萌發(fā)了閱讀源碼窺視實(shí)現(xiàn)細(xì)節(jié)的想法。同時(shí)我們運(yùn)維的 ZooKeeper 集群規(guī)模和數(shù)據(jù)規(guī)模非常大,也想把運(yùn)維的經(jīng)驗(yàn)分享出來供參考去規(guī)避風(fēng)險(xiǎn)點(diǎn)和性能調(diào)優(yōu)。


          2.目標(biāo)讀者


          本文是介紹 ZooKeeper 基礎(chǔ)知識和源碼分析的入門級材料,適合用于初步進(jìn)入分布式系統(tǒng)的開發(fā)人員,以及使用 ZooKeeper 進(jìn)行生產(chǎn)經(jīng)營的應(yīng)用程序運(yùn)維人員。


          Zookeeper系列文章介紹


          第 1 篇:主要介紹 ZooKeeper 使命、地位、基礎(chǔ)的概念和基本組成模塊,以及 ZooKeeper 內(nèi)部運(yùn)行原理,此部分主要從書籍《ZooKeeper 分布式過程協(xié)同技術(shù)詳解》摘錄,對于有 ZooKeeper 基礎(chǔ)的可以略過。堅(jiān)持主要目的,不先陷入解析源碼的繁瑣的實(shí)現(xiàn)上,而是從系統(tǒng)和底層看 ZooKeeper 如何運(yùn)行,通過從高層次介紹其所使用的協(xié)議,以及 ZooKeeper 所采用的在提高性能的同時(shí)還具備容錯(cuò)能力的機(jī)制。


          第 2 篇:簡析 ZooKeeper 的源碼實(shí)現(xiàn),主要目的去介紹 ZooKeeper 集群的工作流程,給出看源碼的簡要指引,能更快上手去深入閱讀源碼。


          第 3 篇:主要介紹業(yè)務(wù)用 zookeeper 做消息系統(tǒng)的實(shí)踐,在實(shí)踐中的優(yōu)化點(diǎn)和踩坑的地方,由于業(yè)務(wù)場景和規(guī)模的差別,關(guān)注點(diǎn)和優(yōu)化點(diǎn)也差別很大,也歡迎在評論區(qū)更新使用 ZooKeeper 共性問題。


          在大數(shù)據(jù)和云計(jì)算盛行的今天,應(yīng)用服務(wù)由很多個(gè)獨(dú)立的程序組成,這些獨(dú)立的程序則運(yùn)行在形形色色,千變?nèi)f化的一組計(jì)算機(jī)上,而如何讓一個(gè)應(yīng)用中的多個(gè)獨(dú)立的程序協(xié)同工作是一件非常困難的事情。而 ZooKeeper 就是一個(gè)分布式的,開放源碼的分布式應(yīng)用程序協(xié)調(diào)服務(wù)。它使得應(yīng)用開發(fā)人員可以更多的關(guān)注應(yīng)用本身的邏輯,而不是協(xié)同工作上。從系統(tǒng)設(shè)計(jì)看,ZooKeeper 從文件系統(tǒng) API 得到啟發(fā),提供一組簡單的 API,使得開發(fā)人員可以實(shí)現(xiàn)通用的協(xié)作任務(wù),例如選舉主節(jié)點(diǎn),管理組內(nèi)成員的關(guān)系,管理元數(shù)據(jù)等,同時(shí) ZooKeeper 的服務(wù)組件運(yùn)行在一組專用的服務(wù)器之上,也保證了高容錯(cuò)性和可擴(kuò)展性。


          本章節(jié)是Zookeeper系列文章的第三篇,本文將為大家解析Zookeeper源碼,幫助大家更好的理解源碼。


          服務(wù)端的線程

          客戶端

          從整體看,客戶端啟動的入口時(shí) ZooKeeperMain,在 ZooKeeperMain 的 run()中,創(chuàng)建出控制臺輸入對象(jline.console.ConsoleReader),然后它進(jìn)入 while 循環(huán),等待用戶的輸入。同時(shí)也調(diào)用 connectToZK 連接服務(wù)器并建立會話(session),在 connect 時(shí)創(chuàng)建 ZooKeeper 對象,在 ZooKeeper 的構(gòu)造函數(shù)中會創(chuàng)建客戶端使用的 NIO socket,并啟動兩個(gè)工作線程 sendThread 和 eventThread,兩個(gè)線程被初始化為守護(hù)線程。

          sendThread 的 run()是一個(gè)無限循環(huán),除非運(yùn)到了 close 的條件,否則他就會一直循環(huán)下去,比如向服務(wù)端發(fā)送心跳,或者向服務(wù)端發(fā)送我們在控制臺輸入的數(shù)據(jù)以及接受服務(wù)端發(fā)送過來的響應(yīng)。

          eventThread 線程負(fù)責(zé)隊(duì)列事件和處理 watch。
          客戶端也會創(chuàng)建一個(gè) clientCnxn,由 ClientCnxnSocketNIO.java 負(fù)責(zé) IO 數(shù)據(jù)通信。

          客戶端的場景說明(事務(wù)、非事務(wù)請求類型)。

          客戶端源碼解析

          ZooKeeperMain 初始化

          ZooKeeper 的構(gòu)造函數(shù),cnxn.start()會創(chuàng)建 sendThread 和 eventThread 守護(hù)線程。

          在 ClientCnxn.java 中,有兩個(gè)重要的數(shù)據(jù)結(jié)構(gòu)。

          /**

          * These are the packets that have been sent and are waiting for a response.

          */

          private final LinkedList pendingQueue = new LinkedList();

          /**

          * These are the packets that need to be sent.

          */

          private final LinkedBlockingDeque outgoingQueue = new LinkedBlockingDeque();

          ZooKeeper 類中的對用戶的輸入?yún)?shù)轉(zhuǎn)換為對 ZK 操作,會調(diào)用 cnxn.submitRequest()提交請求,在 ClientCnxn.java 中會把請求封裝為 Packet 并寫入 outgoingQueue,待 sendThread 線程消費(fèi)發(fā)送給服務(wù)端,對于同步接口,調(diào)用 cnxn.submitRequest()會阻塞,其中客戶端等待是自旋鎖。

          ClientCnxnSocketNIO.java 主要是調(diào)用 dcIO(), 其中讀就緒,讀取服務(wù)端發(fā)送過來的數(shù)據(jù),寫就緒, 往客戶端發(fā)送用戶在控制臺輸入的命令。

          從上面源碼看,客戶端在 cnxn.submitRequest(),會自旋等待服務(wù)端的結(jié)果,直到 Packet 的 finished 被設(shè)置為 true。ClientCnxnSocketNIO.java 調(diào)用 dcIO(),read 邏輯中,會調(diào)用 sendThread.readResponse(), 在 sendThread.readResponse()函數(shù)中的 finally 中調(diào)用 finshPacket()設(shè)置 finished 為 true,進(jìn)而客戶端阻塞解除,返回結(jié)果。

          擴(kuò)展閱讀:

          https://www.cnblogs.com/ZhuChangwu/p/11587615.html


          服務(wù)端和客戶端結(jié)合部分


          會話(Session)

          Client 建立會話的流程如下,
          1. 服務(wù)端啟動,客戶端啟動;
          2. 客戶端發(fā)起 socket 連接;
          3. 服務(wù)端 accept socket 連接,socket 連接建立;
          4. 客戶端發(fā)送 ConnectRequest 給 server;
          5. server 收到后初始化 ServerCnxn,代表一個(gè)和客戶端的連接,即 session,server 發(fā)送 ConnectResponse 給 client;
          6. client 處理 ConnectResponse,session 建立完成。
          客戶端源碼分析

          在 clientCnxn.java 中,run 是一個(gè) while 循環(huán),只要 client 沒有被關(guān)閉會一直循環(huán),每次循環(huán)判斷當(dāng)前 client 是否連接到 server,如果沒有則發(fā)起連接,發(fā)起連接調(diào)用了 startConnect。

          在 connect 是,傳遞了如下參數(shù),

          1. lastZxid:上一個(gè)事務(wù)的 id;
          2. sessionTimeout:client 端配置的 sessionTimeout;
          3. sessId:sessionId,如果之前建立過連接取的是上一次連接的 sessionId
          4. sessionPasswd:session 的密碼;

          服務(wù)端源碼分析

          server 在啟動后,會暴露給客戶端連接的地址和端口以提供服務(wù)。我們先看一下NIOServerCnxnFactory,主要是啟動三個(gè)線程。

          1. AcceptThread:用于接收 client 的連接請求,建立連接后交給 SelectorThread 線程處理

          2. SelectorThread:用于處理讀寫請求

          3. ConnectionExpirerThread:檢查 session 連接是否過期

          client 發(fā)起 socket 連接的時(shí)候,server 監(jiān)聽了該端口,接收到 client 的連接請求,然后把建立練級的 SocketChannel 放入隊(duì)列里面,交給 SelectorThread 處理。

          SelectorThread 是一個(gè)不斷循環(huán)的線程,每次循環(huán)都會處理剛剛建立的 socket 連接。

          session 生成算法

          監(jiān)視(Watch)

          本小節(jié)主要看看 ZooKeeper 怎么設(shè)置監(jiān)視和監(jiān)控點(diǎn)的通知。ZooKeeper 可以定義不同類型的通知,如監(jiān)控 znode 的數(shù)據(jù)變化,監(jiān)控 znode 子節(jié)點(diǎn)的變化,監(jiān)控 znode 的創(chuàng)建或者刪除。ZooKeeper 的服務(wù)端實(shí)現(xiàn)了監(jiān)視點(diǎn)管理器(watch manager)。

          一個(gè) WatchManager 類的實(shí)例負(fù)責(zé)管理當(dāng)前已經(jīng)注冊的監(jiān)視點(diǎn)列表,并負(fù)責(zé)觸發(fā)他們,監(jiān)視點(diǎn)只會存在內(nèi)存且為本地服務(wù)端的概念,所有類型的服務(wù)器都是使用同樣的方式處理監(jiān)控點(diǎn)。

          DataTree 類中持有一個(gè)監(jiān)視點(diǎn)管理器來負(fù)責(zé)子節(jié)點(diǎn)監(jiān)控和數(shù)據(jù)的監(jiān)控。

          在服務(wù)端觸發(fā)一個(gè)監(jiān)視點(diǎn),最終會傳播到客戶端,負(fù)責(zé)處理傳播的為服務(wù)端的 cnxn 對象(ServerCnxn 類),此對象表示客戶端和服務(wù)端的連接并實(shí)現(xiàn)了 Watcher 接口。Watch.process 方法序列化了監(jiān)視點(diǎn)事件為一定的格式,以便于網(wǎng)絡(luò)傳送。ZooKeeper 客戶端接收序列化的監(jiān)視點(diǎn)事件,并將其反序列化為監(jiān)控點(diǎn)事件的對象,并傳遞給應(yīng)用程序。

          以同步版本的 GetData 為例
          Watcher 接口的定義,如果設(shè)置了監(jiān)視點(diǎn),我們要實(shí)現(xiàn) process 函數(shù)。

          客戶端 Watcher 的 process()接口

          客戶端 watcher 實(shí)現(xiàn)

          在客戶端 GetData 時(shí),如果注冊 watch 監(jiān)控點(diǎn)到服務(wù)端,在 watch 的 path 的 value 變化時(shí),服務(wù)端會通知客戶端該變化。
          在客戶端的 GetData 方法中(ZooKeeper 類的 GetData):
          • 創(chuàng)建 WatchRegistration wcb= new DataWatchRegistration(watcher, clientPath),path 和 watch 封裝進(jìn)了一個(gè)對象;
          • 創(chuàng)建一個(gè) request,設(shè)置 type 為 GetData 對應(yīng)的數(shù)值;
          • request.setWatch(watcher != null),setWatch 參數(shù)為一個(gè) bool 值。
          • 調(diào)用 ClientCnxn.submitRequest(...) , 將請求包裝為 Packet,queuePacket()方法的參數(shù)中存在創(chuàng)建的 path+watcher 的封裝類 WatchRegistration,請求會被 sendThread 消費(fèi)發(fā)送到服務(wù)端。

          客戶端 GetData()

          客戶端 submitRequest()
          服務(wù)端如何處理 GetData 請求呢,由于是讀請求,我們可以直接看 FinalRequestProcessor 處理器的 public void processRequest(Request request){}方法,看它針對 GetData()方式的請求做出了哪些動作。
          zks.getZKDatabase().getData(getDataRequest.getPath(), stat, getDataRequest.getWatch() ? cnxn : null)
          根據(jù) watcher 的“有無”給服務(wù)端添加不同的 Watcher。服務(wù)端 GetData()函數(shù),在服務(wù)端維護(hù)了一份 path+watcher 的 map,如果設(shè)置 watcher,服務(wù)端會保存該 path 的 watcher。

          服務(wù)端 GetData()

          服務(wù)端 GetData()

          服務(wù)端 addWatch ()

          為了測試服務(wù)端監(jiān)視通知客戶端,我們在客戶端本地輸入的命令,

          set /path newValue

          客戶端 SetData()

          從 SetData 的源碼看,本次的 submitRequest 參數(shù)中,WatchRegistration==null,可以推斷,服務(wù)端在 FinalRequestProcessor 中再處理時(shí)取出的 watcher==null,也就不會將 path+watcher 保存進(jìn) maptable 中,其他的處理過程和上面 GetData 類似。

          服務(wù)端在滿足觸發(fā)監(jiān)控點(diǎn)時(shí),并通過 cnxn 的 process()方法處理(NIOServerCnxn 類)通知到客戶端。在服務(wù)端處理的 SetData()函數(shù)看,Set 數(shù)值后,會觸發(fā) watch 回調(diào),即 triggerWatch()。

          服務(wù)端 SetData()

          服務(wù)端 triggerWatch ()

          服務(wù)端 NIOServerCnxn 的 process()

          從上面看服務(wù)端在往客戶端發(fā)送事務(wù)型消息, 并且 new ReplyHeader(-1, -1L,0)第一個(gè)位置上的參數(shù)是-1。

          在客戶端的 SendThread 讀就緒源碼部分(readResponse),在 readResponse 函數(shù)中會判斷 xid==-1 時(shí)然后調(diào)用 eventThread.queueEvent(we ),把響應(yīng)交給 EventThread 處理。

          其 eventThread 是一個(gè)守護(hù)線程,run()函數(shù)在 while(true)去消費(fèi) waitingEvents,最終調(diào)用會 watcher.process(pair.event),其中 process 是 watcher 的 process 的接口的實(shí)現(xiàn),從而完成 wacher 回調(diào)處理。

          客戶端 eventThread 的 run()

          客戶端 processEvent()

          客戶端接口 watch process()


          ZooKeeper 實(shí)踐經(jīng)驗(yàn)


          業(yè)務(wù)的控制面架構(gòu)

          控制面架構(gòu)
          在業(yè)務(wù)處理邏輯中,API 會寫 ZooKeeper 和 db 的,agent 作為客戶端連接 ZooKeeper 集群,并注冊 watch 到感興趣的節(jié)點(diǎn),在 watch 的 znode 發(fā)生變化時(shí),服務(wù)端觸發(fā)通知 agent,agent 感知到數(shù)據(jù)變化,經(jīng)過數(shù)據(jù)轉(zhuǎn)換,再通過適當(dāng)?shù)慕涌谙掳l(fā)到設(shè)備上。
          ZooKeeper 部署結(jié)構(gòu)
          客戶端會根據(jù)域名解析訪問 Observer,客戶端不會直接連接主集群,做到讀寫分離。

          ZooKeeper 集群的特點(diǎn)

          目前我們運(yùn)維的 ZooKeeper 集群規(guī)模大,客戶端數(shù)目也很大,導(dǎo)致 znode 數(shù)目和 watcher 數(shù)目也是巨大的,這個(gè)運(yùn)維帶來重大的挑戰(zhàn)。

          ZooKeeper 集群規(guī)模,以地域級集群舉例(2020 前)

          地域集群規(guī)模(設(shè)備數(shù)目)備注
          上海168
          廣州95
          北京41
          其他4 ~ 12

          ZooKeeper 集群的 Znode 數(shù)目

          該設(shè)備管理的 znode 節(jié)點(diǎn)數(shù)目高達(dá) 3 千萬+,同時(shí)我們也可以看出 znode 節(jié)點(diǎn)在動態(tài)的變化,波谷在晚上,這些變化就是用戶進(jìn)行擴(kuò)縮容。

          ZooKeeper 集群的 Watch 數(shù)目

          選擇單臺設(shè)備看

          該設(shè)備管理的 watcher 節(jié)點(diǎn)數(shù)目高達(dá) 1.6 億 watch 數(shù)目,同時(shí)我們也可以看出 watch 節(jié)點(diǎn)在動態(tài)的變化,波谷在晚上,這些變化就是用戶進(jìn)行擴(kuò)縮容。

          實(shí)踐場景分析和優(yōu)化措施

          災(zāi)備集群搭建

          痛點(diǎn):由于各種原因ZooKeeper 集群可能發(fā)起重新選舉,并且在選舉過程中,集群服務(wù)會不可用,更有甚者,長時(shí)間選舉不出來 Leader,需要重啟集群。同時(shí),現(xiàn)網(wǎng)會遇到機(jī)房裁撤,需要遷移 ZooKeeper 的服務(wù)器,特別是 3.5 版本前,ZooKeeper 沒有提供重配置(reconfig),在遷移集群時(shí),需要復(fù)雜的啟用服務(wù)器,風(fēng)險(xiǎn)很大。

          在現(xiàn)網(wǎng)運(yùn)營中,出現(xiàn)過半個(gè)小時(shí)以上,服務(wù)不可用的情況,災(zāi)備集群的搭建顯得十分重要。

          ZooKeeper 數(shù)據(jù)存儲的一個(gè)優(yōu)點(diǎn)是,數(shù)據(jù)的存儲方式是一樣的,通過事務(wù)日志和快照的合并可以得到正確的數(shù)據(jù)視圖,可以拷貝日志文件和快照文件到另外的新集群。

          目前我們切換新舊集群還是人工參與,不過可以大幅度降低服務(wù)不可用的整體時(shí)間。在搭建災(zāi)備集群時(shí),也會遇到環(huán)境,配置,機(jī)型等問題,需要在實(shí)踐中摸索,并能熟練的切換。

          Observer 單核高負(fù)載時(shí) Observer 數(shù)據(jù)落地慢

          觸發(fā)點(diǎn)

          ZK 數(shù)據(jù)有突發(fā)寫入時(shí),子樹數(shù)據(jù)量大。

          故障現(xiàn)象

          客戶端感知數(shù)據(jù)變化慢,下發(fā)配置不及時(shí),導(dǎo)致用戶業(yè)務(wù)受影響。

          故障過程

          1. ZooKeeper 數(shù)據(jù)有突發(fā)寫入時(shí);

          2. 客戶端從 Observer 拉取大子樹(children 很多的節(jié)點(diǎn)的 children 列表);

          3. 觸發(fā) Observer 發(fā)生單核高負(fù)載,高負(fù)載 CPU 主要處理 getChildren 時(shí)的數(shù)據(jù)序列化去了;

          4.客戶端看見從 Observer getChildren 回來的數(shù)據(jù)是很舊的數(shù)據(jù),而此時(shí) ZooKeeper 數(shù)據(jù)早就寫入主集群了;

          5.客戶端一次不能看見的數(shù)據(jù)變化特別慢,導(dǎo)致客戶端花了很長時(shí)間才感知并在本地處理完這些突發(fā)寫入。

          故障原因分析

          寫子樹時(shí),觸發(fā)客戶端的 Children 事件,由于 ZooKeepeer 實(shí)現(xiàn)的機(jī)制不能單獨(dú)通知哪個(gè) Children 節(jié)點(diǎn)變化,客戶端必須自己去 getChildren 獲得全量的 Children 節(jié)點(diǎn)(例如 Children 層機(jī)有 10w 節(jié)點(diǎn),在新增一個(gè)節(jié)點(diǎn),客戶端需要下拉 10w+的數(shù)據(jù)到本地),如果 Children 數(shù)量很大,會極大消耗 Observer 的性能,在 Observer 高負(fù)載后處理不及時(shí),導(dǎo)致下發(fā)配置延時(shí)。

          優(yōu)化措施

          1.擴(kuò)容 Observer,并進(jìn)行監(jiān)控 Observer 的狀態(tài)(cpu,內(nèi)存);
          2.大量子節(jié)點(diǎn)樹二級分組優(yōu)化,把 getChildren 拉取數(shù)據(jù)的規(guī)模降低。
          3.客戶端開啟多進(jìn)程,根據(jù)適當(dāng)?shù)闹笜?biāo)分組,然后分配到不同進(jìn)程去管理節(jié)點(diǎn),可以加速并發(fā),進(jìn)程的管理節(jié)點(diǎn)規(guī)模要盡量均衡性。
          4.客戶端可以延時(shí)拉取,例如如果要插入 10 個(gè)節(jié)點(diǎn),在獲得第一次 watch 通知后可以 hold 一個(gè)隨機(jī)事件,再去拉取數(shù)據(jù),這樣在 hold time 時(shí),節(jié)點(diǎn)現(xiàn)象變化完成,可以一下子拉取到所有變化現(xiàn)象,而不是在每個(gè)節(jié)點(diǎn)變化時(shí)都 get 一次,加大對服務(wù)端的壓力,不過這個(gè) hold time 的是否開啟要根據(jù)具體的業(yè)務(wù)場景決定。

          服務(wù)器 Full GC 導(dǎo)致會話異常

          觸發(fā)點(diǎn)

          ZooKeeper 的服務(wù)端機(jī)器發(fā)生了 gc,gc 時(shí)間過長,gc 結(jié)束后發(fā)生會話超時(shí)處理。

          故障現(xiàn)象

          長時(shí)間的 gc 后,會話超時(shí),客戶端再請求服務(wù)器時(shí),遇到異常,客戶端會重啟。服務(wù)端斷開大量的客戶端時(shí),會帶來連接沖擊。

          機(jī)房網(wǎng)絡(luò)中斷,大量連接沖擊 Observer

          觸發(fā)點(diǎn)

          客戶端,Observer,主集群跨區(qū)部署,某區(qū)機(jī)房網(wǎng)絡(luò)短暫中斷。

          連接沖擊現(xiàn)象

          集群有連接沖擊發(fā)生時(shí),closeSession 事務(wù)導(dǎo)致所有 Observer 無法快速處理新建的連接和其他請求,從而客戶端主動斷連,又出現(xiàn)更多的 closeSession。幾乎無法自行恢復(fù)。

          單臺 Observer 臨時(shí)節(jié)點(diǎn)的數(shù)量變化

          集群中 Fellower 數(shù)量變化

          故障過程

          階段 1:網(wǎng)絡(luò)異常,Observer 和主集群的通信中斷,Leader 把 Observer 踢出集群(從上圖的Fellower 的數(shù)量變化可以看出),大量客戶端開始斷連(從上圖的臨時(shí)節(jié)點(diǎn)的數(shù)量變化可以看出);

          階段 2: 網(wǎng)絡(luò)恢復(fù)后 Observer 感知到了被踢出,進(jìn)入自恢復(fù)邏輯;

          階段 3: Observer 同步完新事務(wù),并進(jìn)入 Serving 狀態(tài);

          階段 4: 大量客戶端開始重連 Observer,Observer 沒有限制住連接沖擊導(dǎo)致卡死。

          故障原因

          在階段 4,觀察分析 Observer 的 pps 不是很高,不過處理事務(wù)非常慢,線程棧發(fā)現(xiàn)有兩個(gè)線程互相卡慢,使得 closeSession 事務(wù)無法在 Observer 上有效執(zhí)行,也使 NIO 連接接入層線程無法處理連接的數(shù)據(jù)接收和數(shù)據(jù)回復(fù)和建立新連接。

          優(yōu)化措施

          限制或者抑制連接沖擊。在故障時(shí),根據(jù) tcp 狀態(tài)為 established 的連接數(shù)量動態(tài)限制連接,不過 established 的連接數(shù)量其未過閥值,但是觀察到 fd 仍是滿的,大部分連接處于 tcp 的 close-wait 狀態(tài),其中 fd 消耗過多,如果 Observer 落地日志的話,也會造成寫 binlog 或 snapshot 失敗導(dǎo)致進(jìn)程異常退出。

          initlimit 和 syncLimit 參數(shù)配置對集群和會話的影響

          initLimit 參數(shù)

          initLimit 是追隨者最初連接到群首時(shí)的超時(shí)值,單位為 tick 值的倍數(shù)。當(dāng)某個(gè)追隨者最初與群首建立連接時(shí),它們之間會傳輸相當(dāng)多的數(shù)據(jù),尤其是追隨者落后整體很多時(shí)。配置 initLimit 參數(shù)值取決于群首與追隨者之間的網(wǎng)絡(luò)傳輸速度情況,以及傳輸?shù)臄?shù)據(jù)量大小。如果 ZooKeeper 中保存的數(shù)據(jù)量特別大時(shí)或者網(wǎng)絡(luò)非常緩慢時(shí),就需要增大 initLimit。

          故障場景:在相同數(shù)據(jù)量的情況下,對于一個(gè)正常運(yùn)行中的 3 節(jié)點(diǎn)主集群,如果一臺 follower 重啟或一臺 observer 想要加入集群:initLimit 過小,會使這臺機(jī)器無法加入主集群。

          原因分析

          ZooKeeper 的 3.4.4 版本的 observer/follower 啟動時(shí)會讀取一次 snapshot,在選舉邏輯知道 leader 信息后,與 leader quorum 端口(2001、2888)交互前,還會再讀取一次 snapshot。

          另外,initLimit 影響 leader 對 observer/follower 的 newLeaderAck(ZooKeeper3.4.4 或 3.4.6 版本),成員加入集群前,成員機(jī)器上會進(jìn)行一次 snapshot 刷出,耗時(shí)如果過長,會使 leader 對 observer 或 follower 的的 newLeaderAck 讀取超時(shí)(tickTime*initLimit)。如果此時(shí)正處理 leader 剛選舉完要給一個(gè) follower 同步數(shù)據(jù)的時(shí)候,還會導(dǎo)致 leader 不能及時(shí)收到足夠數(shù)量的 newLeaderAck 而導(dǎo)致集群組建失敗。

          在 ZooKeePeer 的 3.5 版本后,初始化加載 snapshot 只會加載一次,不過需要同步的數(shù)據(jù)量比較大時(shí),initLimit 還是要調(diào)大一些。

          syncLimit 參數(shù)

          syncLimit 是追隨者與群首進(jìn)行 sync 操作時(shí)的超時(shí)值,單位為 tick 值的倍數(shù)。

          追隨者總是會稍微落后于群首,但是因?yàn)榉?wù)器負(fù)載或者網(wǎng)絡(luò)問題,就會導(dǎo)致追隨者落后群首太多,甚至需要放棄該追隨者,如果群首與追隨者無法進(jìn)行 sync 操作,而且超過了 syncLimit 的 tick 時(shí)間,就會放棄該追隨者。

          優(yōu)化措施:
          1. 測試追隨者與群首的網(wǎng)絡(luò)情況,進(jìn)行規(guī)劃配置,并實(shí)時(shí)監(jiān)控集群數(shù)據(jù)量的變化。

          2. 提高服務(wù)端的性能,網(wǎng)卡性能。



          One more thing


          目前,騰訊云微服務(wù)引擎(Tencent Cloud Service Engine,簡稱TSE)已上線,并發(fā)布子產(chǎn)品服務(wù)注冊、配置中心(ZooKeeper/Nacos/Eureka/Apollo)、治理中心(PolarisMesh)。支持一鍵創(chuàng)建、免運(yùn)維、高可用、開源增強(qiáng)的組件托管服務(wù),歡迎點(diǎn)擊文末的「閱讀原文」了解詳情并使用!


          TSE官網(wǎng)地址:

          https://cloud.tencent.com/product/tse


          參考文獻(xiàn)

          • ZooKeeper-選舉實(shí)現(xiàn)分析:
            https://juejin.im/post/5cc2af405188252da4250047
          • Apache ZooKeeper 官網(wǎng):
            https://zookeeper.apache.org/
          • ZooKeeper github:
            https://github.com/apache/zookeeper
          • 《zookeeper-分布式過程協(xié)同技術(shù)詳解》【美】里德,【美】Flavio Junqueira 著
          • ZooKeeper 源碼分析:
            https://blog.reactor.top/tags/Zookeeper/
          • ZooKeeper-選舉實(shí)現(xiàn)分析:
            https://juejin.im/post/5cc2af405188252da4250047
          • ZooKeeper 源碼分析:
            https://www.cnblogs.com/sunshine-2015/tag/zookeeper/

          往期

          推薦


          《騰訊云消息隊(duì)列 TDMQ Pulsar 版商業(yè)化首發(fā)|持續(xù)提供高性能、強(qiáng)一致的消息服務(wù)》

          《深入理解Rabbit MQ與AMQP協(xié)議》

          《應(yīng)用多環(huán)境部署的最佳實(shí)踐》

          《單元化架構(gòu)在金融行業(yè)的最佳實(shí)踐》

          《服務(wù)器又崩了?深度解析高可用架構(gòu)的挑戰(zhàn)和實(shí)踐》

          《Kratos技術(shù)系列|從Kratos設(shè)計(jì)看Go微服務(wù)工程實(shí)踐》

          《Pulsar技術(shù)系列 - 深度解讀Pulsar Schema》

          《Apache Pulsar事務(wù)機(jī)制原理解析|Apache Pulsar 技術(shù)系列》




          掃描下方二維碼關(guān)注本公眾號,

          了解更多微服務(wù)、消息隊(duì)列的相關(guān)信息!

          解鎖超多鵝廠周邊!

          戳原文,查看更多微服務(wù)引擎TSE信息!


          點(diǎn)個(gè)在看你最好看


          瀏覽 53
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日逼中文字幕 | 人人人人摸 | 久久综合色鬼 | 91五月婷婷 | 亚洲爽 |