1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        揭秘 Nacos 的 AP 架構(gòu) 「Distro 一致性協(xié)議」

        共 5734字,需瀏覽 12分鐘

         ·

        2022-05-14 02:14


        前言

        上篇我們講解了 Nacos 的架構(gòu)原理:一條注冊(cè)請(qǐng)求會(huì)經(jīng)歷什么。

        Nacos 架構(gòu)原理①:一條注冊(cè)請(qǐng)求會(huì)經(jīng)歷什么?

        這次我們要進(jìn)入 Nacos 的一致性底層原理了,還是先來(lái)一張架構(gòu)圖,讓大家對(duì) Nacos 的架構(gòu)有個(gè)整體的印象,本篇會(huì)主要講解一致性模塊中的?Distro?協(xié)議。

        上篇留了兩個(gè)知識(shí)點(diǎn)

        • ① 服務(wù)實(shí)例注冊(cè)到 Nacos 節(jié)點(diǎn)后,通過(guò) UDP 方式推送到所有服務(wù)實(shí)例。讓其他服務(wù)實(shí)例感知到服務(wù)列表的變化。
        • ② 如何復(fù)制數(shù)據(jù)到其他節(jié)點(diǎn):當(dāng)前 Nacos 節(jié)點(diǎn)開(kāi)啟 1s 的延遲任務(wù),將數(shù)據(jù)同步給其他 Nacos 節(jié)點(diǎn)。(分區(qū)一致性)

        第 ② 個(gè)知識(shí)點(diǎn)就是 Nacos 自研的?Distro?一致性協(xié)議的核心功能。

        首先這個(gè) Distro 協(xié)議是針對(duì)集群環(huán)境的,比如下面這三個(gè)集群節(jié)點(diǎn)組成了一個(gè)集群。服務(wù) A 和服務(wù) B 會(huì)往這個(gè)集群進(jìn)行注冊(cè)。

        Nacos 集群節(jié)點(diǎn)
        Nacos 集群環(huán)境

        我們知道 Nacos 它是支持兩種分布式定理的:CP(分區(qū)一致性)和?AP(分區(qū)可用性) ,而 AP 是通過(guò) Nacos 自研的?Distro?協(xié)議來(lái)保證的,CP 是通過(guò) Nacos 的?JRaft?協(xié)議來(lái)保證的。

        因?yàn)樽?cè)中心作為系統(tǒng)中很重要的的一個(gè)服務(wù),需要盡最大可能對(duì)外提供可用的服務(wù),所以選擇 AP 來(lái)保證服務(wù)的高可用,另外 Nacos 還采取了心跳機(jī)制來(lái)自動(dòng)完成服務(wù)數(shù)據(jù)補(bǔ)償?shù)臋C(jī)制,所以說(shuō) Distro 協(xié)議是弱一致性的。

        如果采用 CP 協(xié)議,則需要當(dāng)前集群可用的節(jié)點(diǎn)數(shù)過(guò)半才能工作。

        關(guān)于 CP 和 AP 的理論知識(shí),可以參考這篇:用太極拳講分布式理論 CAP 和 BASE,真舒服!

        問(wèn)題:Nacos 哪些地方用到了 AP 和 CP?

        • 針對(duì)臨時(shí)服務(wù)實(shí)例,采用?AP?來(lái)保證注冊(cè)中心的可用性,Distro?協(xié)議。
        • 針對(duì)持久化服務(wù)實(shí)例,采用?CP?來(lái)保證各個(gè)節(jié)點(diǎn)的強(qiáng)一致性,JRaft?協(xié)議。(JRaft 是 Nacos 對(duì) Raft 的一種改造)
        • 針對(duì)配置中心,無(wú) Database 作為存儲(chǔ)的情況下,Nacos 節(jié)點(diǎn)之間的內(nèi)存數(shù)據(jù)為了保持一致,采用?CP。Nacos 提供這種模式只是為了方便用戶本機(jī)運(yùn)行,降低對(duì)存儲(chǔ)依賴,生產(chǎn)環(huán)境一般都是通過(guò)外置存儲(chǔ)組件來(lái)保證數(shù)據(jù)一致性。
        • 針對(duì)配置中心,有 Database 作為存儲(chǔ)的情況下,Nacos 通過(guò)持久化后通知其他節(jié)點(diǎn)到數(shù)據(jù)庫(kù)拉取數(shù)據(jù)來(lái)保證數(shù)據(jù)一致性,另外采用讀寫分離架構(gòu)來(lái)保證高可用,所以這里我認(rèn)為這里采用的?AP,歡迎探討。
        • 針對(duì)?異地多活,采用?AP?來(lái)保證高可用。

        弦外音:

        臨時(shí)服務(wù)實(shí)例就是我們默認(rèn)使用的 Nacos 注冊(cè)中心模式,客戶端注冊(cè)后,客戶端需要定時(shí)上報(bào)心跳信息來(lái)進(jìn)行服務(wù)實(shí)例續(xù)約。這個(gè)在注冊(cè)的時(shí)候,可以通過(guò)傳參設(shè)置是否是臨時(shí)實(shí)例。

        持久化服務(wù)實(shí)例就是不需要上報(bào)心跳信息的,不會(huì)被自動(dòng)摘除,除非手動(dòng)移除實(shí)例,如果實(shí)例宕機(jī)了,Nacos 只會(huì)將這個(gè)客戶端標(biāo)記為不健康。

        本篇會(huì)帶著大家從源碼角度來(lái)深入剖析下 Distro 協(xié)議。

        知識(shí)點(diǎn)預(yù)告:

        • ① Distro 的設(shè)計(jì)思想和六大機(jī)制。
        • ② Nacos 如何同步數(shù)據(jù)到其他節(jié)點(diǎn)。(異步復(fù)制機(jī)制,本篇重點(diǎn)講解)
        • ③ Nacos 如何保證所有節(jié)點(diǎn)的數(shù)據(jù)一致性。(定期檢驗(yàn);健康檢查機(jī)制,下一篇重點(diǎn)講解)
        • ④ 新加入的 Nacos 節(jié)點(diǎn),如何進(jìn)行拉取數(shù)據(jù)。(新節(jié)點(diǎn)同步機(jī)制)

        一、Distro 的設(shè)計(jì)思想和六大機(jī)制

        Distro?協(xié)議是 Nacos 對(duì)于臨時(shí)實(shí)例數(shù)據(jù)開(kāi)發(fā)的一致性協(xié)議。

        Distro 協(xié)議是集 Gossip + Eureka 協(xié)議的優(yōu)點(diǎn)并加以優(yōu)化后出現(xiàn)的。

        關(guān)于 Gossip 協(xié)議,可以看這篇:病毒入侵:全靠分布式 Gossip 協(xié)議

        Gossip 協(xié)議有什么坑?由于隨機(jī)選取發(fā)送的節(jié)點(diǎn),不可避免地存在消息重復(fù)發(fā)送給同一節(jié)點(diǎn)的情況,增加了網(wǎng)絡(luò)的傳輸?shù)膲毫Γo消息節(jié)點(diǎn)帶來(lái)額外的處理負(fù)載。

        Distro 協(xié)議的優(yōu)化:每個(gè)節(jié)點(diǎn)負(fù)責(zé)一部分?jǐn)?shù)據(jù),然后將數(shù)據(jù)同步給其他節(jié)點(diǎn),有效地降低了消息冗余的問(wèn)題。

        關(guān)于臨時(shí)實(shí)例數(shù)據(jù):臨時(shí)數(shù)據(jù)其實(shí)是存儲(chǔ)在內(nèi)存緩存中的,并且在其他節(jié)點(diǎn)在啟動(dòng)時(shí)會(huì)進(jìn)行全量數(shù)據(jù)同步,然后節(jié)點(diǎn)也會(huì)定期進(jìn)行數(shù)據(jù)校驗(yàn)。

        大家不要被這個(gè)協(xié)議嚇到,其實(shí)就是阿里自己實(shí)現(xiàn)的一套同步邏輯。

        AP 中的 P 代表網(wǎng)絡(luò)分區(qū),所以 Distro 在分布式集群環(huán)境下才能真正發(fā)揮其作用。它保證了在多個(gè) Nacos 節(jié)點(diǎn)組成的 Nacos 集群環(huán)境中,當(dāng)其中某個(gè) Nacos 宕機(jī)后,整個(gè)集群還是能正常工作。

        Distro 的設(shè)計(jì)機(jī)制

        • 平等機(jī)制:Nacos 的每個(gè)節(jié)點(diǎn)是平等的,都可以處理寫的請(qǐng)求。(上一講已經(jīng)重點(diǎn)講解了?)
        • 異步復(fù)制機(jī)制:Nacos 把變更的數(shù)據(jù)異步復(fù)制到其他節(jié)點(diǎn)。(??重點(diǎn)講解)
        • 健康檢查機(jī)制:每個(gè)節(jié)點(diǎn)只存了部分?jǐn)?shù)據(jù),定期檢查客戶端狀態(tài)保持?jǐn)?shù)據(jù)一致性。
        • 本地讀機(jī)制:?每個(gè)節(jié)點(diǎn)獨(dú)立處理讀請(qǐng)求,及時(shí)從本地發(fā)出響應(yīng)。
        • 新節(jié)點(diǎn)同步機(jī)制:Nacos 啟動(dòng)時(shí),從其他節(jié)點(diǎn)同步數(shù)據(jù)。
        • 路由轉(zhuǎn)發(fā)機(jī)制:客戶端發(fā)送的寫請(qǐng)求,如果屬于自己則處理,否則路由轉(zhuǎn)發(fā)給其他節(jié)點(diǎn)。(上一講已經(jīng)重點(diǎn)講解了?)
        Distro 的設(shè)計(jì)機(jī)制

        二、異步復(fù)制機(jī)制:寫入數(shù)據(jù)后如何同步給其他節(jié)點(diǎn)

        2.1 核心入口

        核心源碼路徑:

        /naming/consistency/ephemeral/distro/DistroConsistencyServiceImpl.java

        這個(gè)類的名字就說(shuō)明它是 Distro 一致性協(xié)議的接口實(shí)現(xiàn)類。

        當(dāng)注冊(cè)請(qǐng)求交給 Nacos 節(jié)點(diǎn)來(lái)處理時(shí),核心入口方法就是 put(),如下圖所示:

        上一講我們已經(jīng)說(shuō)過(guò),這里面會(huì)做幾件事:

        添加實(shí)例信息的流程
        • ① 將實(shí)例信息存放到內(nèi)存緩存 concurrentHashMap 里面。
        • ② 添加一個(gè)任務(wù)到 BlockingQueue 里面,這個(gè)任務(wù)就是將最新的實(shí)例列表通過(guò) UDP 的方式推送給所有客戶端(服務(wù)實(shí)例),這樣客戶端就拿到了最新的服務(wù)實(shí)例列表,緩存到本地。
        • ③ 開(kāi)啟 1s 的延遲任務(wù),將數(shù)據(jù)通過(guò)給其他 Nacos 節(jié)點(diǎn)。

        說(shuō)明:第二件事是 Nacos 和 客戶端如何保持?jǐn)?shù)據(jù)一致性的,第三件事是 Nacos 集群間如何保持?jǐn)?shù)據(jù)一致性的,因本篇重點(diǎn)講解 Nacos 的 AP 原理,所以會(huì)針對(duì)第三件事來(lái)進(jìn)行闡述。而第二件事,會(huì)在后續(xù)文章中重點(diǎn)講解。

        2.2 sync 方法的參數(shù)說(shuō)明

        首先我們來(lái)看下 distroProtocol.sync(),這個(gè)方法傳了哪些參數(shù):

        • 第一個(gè)參數(shù) new DistroKey(),它里面?zhèn)髁?key 和一個(gè)常量。

        key:就是客戶端的服務(wù)名,示例值如下:

        com.alibaba.nacos.naming.iplist.ephemeral.public##DEFAULT_GROUP@@nacos.naming.serviceName

        INSTANCE_LIST_KEY_PREFIX:就是 com.alibaba.nacos.naming.iplist.

        然后這兩個(gè)參數(shù)組裝成一個(gè) DistroKey。

        • 第二個(gè)參數(shù)是同步數(shù)據(jù)的類型,這里為 change。

        • 第三個(gè)參數(shù)是同步任務(wù)的延遲時(shí)間,1s。

        2.3 sync 的核心邏輯:添加任務(wù)

        先上一張?jiān)韴D幫助大家理解,流程圖如下所示。核心邏輯分為以下幾步。

        • 遍歷其他節(jié)點(diǎn),拿到節(jié)點(diǎn)信息。
        • 判斷這個(gè)任務(wù)在 map 中是否存在,如果存在則合并這個(gè) task。
        • 如果不存在,則加到 map 中。
        • 后臺(tái)線程遍歷這個(gè) map,拿到任務(wù)。
        添加任務(wù)到 map 中

        代碼的時(shí)序圖如下所示:

        sync 的核心代碼時(shí)序圖
        • 第一個(gè)類?DistroConsistencyServiceImpl?把實(shí)例信息加入 map 中,后續(xù)通過(guò)?UDP方式推送給客戶端。

        • 第二個(gè)類?DistroProtocol?主要就是循環(huán)遍歷其他節(jié)點(diǎn)。

        • 第三個(gè)類?NacosDelayTaskExecuteEngine?是核心類,創(chuàng)建了一個(gè)同步的任務(wù)到?ConcurrentHashMap?中。

        2.4 sync 的核心邏輯:后臺(tái)線程異步復(fù)制數(shù)據(jù)

        先說(shuō)下哈,這個(gè)核心邏輯極其復(fù)雜,我們看的時(shí)候需要抓主線,知道其中幾個(gè)關(guān)鍵點(diǎn)就可以了。

        悟空在畫代碼邏輯圖的時(shí)候,內(nèi)心是崩潰的,Nacos 為什么寫這么復(fù)雜啊!大家不用細(xì)看,看了也會(huì)懵??,理解核心步驟就可以了。(圖中有個(gè)小細(xì)節(jié),我對(duì)不同的類進(jìn)行了顏色區(qū)分)

        核心步驟

        • 遍歷其他節(jié)點(diǎn),創(chuàng)建一個(gè)同步的任務(wù),加到 map 中。

        • 后臺(tái)線程不斷從 map 中拿到 task,然后移除這個(gè) task。

        • 把這個(gè) task 加到一個(gè)隊(duì)列里面。

        • 有個(gè) worker 專門從隊(duì)列里面拿到 task 來(lái)執(zhí)行。

        • 這個(gè) task 就是發(fā)送 http 請(qǐng)求給其他節(jié)點(diǎn),請(qǐng)求參數(shù)中包含注冊(cè)的實(shí)例信息(序列化后的二進(jìn)制數(shù)據(jù))。拼接的請(qǐng)求 url 地址為:

          http://192.168.0.101:8858/nacos/v1/ns/distro/datum

        Nacos 異步復(fù)制數(shù)據(jù)到其他節(jié)點(diǎn)的流程圖如下:

        Nacos 異步復(fù)制數(shù)據(jù)到其他節(jié)點(diǎn)的流程圖

        2.5 其他節(jié)點(diǎn)如何處理同步請(qǐng)求

        2.5.1 如何存儲(chǔ)注冊(cè)信息

        處理同步請(qǐng)求的邏輯還是比較簡(jiǎn)單的,就是把注冊(cè)信息存起來(lái),然后同步到其他客戶端。

        注冊(cè)信息會(huì)存放到一個(gè)?datum?中,然后 datum 放到一個(gè)?dataStore?中。datum 和 dataStore 的數(shù)據(jù)結(jié)構(gòu)如下圖所示:

        • datum 包含 value、key、timestamp。value 就是注冊(cè)的客戶端信息(是一個(gè) ArrayList)

        • datastore 是一個(gè) ConcurrentHashMap,包含多個(gè) datum。

        存儲(chǔ)注冊(cè)信息的數(shù)據(jù)結(jié)構(gòu)

        2.5.2 源碼分析

        根據(jù) 2.4 講到的請(qǐng)求的 URL:/nacos/v1/ns/distro/datum,處理這個(gè)請(qǐng)求的類為

        com/alibaba/nacos/naming/controllers/DistroController.java

        入口方法為?onSyncDatum,里面做的主要事情如下:

        • ① 把實(shí)例信息放入到一個(gè) datum 內(nèi)存中,然后又存放到 DataStore 的結(jié)構(gòu)中,而 DataStore 的本質(zhì)就是一個(gè)?ConcurrentHashMap。

        • ② 將注冊(cè)信息通過(guò) UDP 的方式推送給客戶端。

        服務(wù)端處理注冊(cè)請(qǐng)求的源碼

        三、定時(shí)同步:如何保持?jǐn)?shù)據(jù)一致性

        3.1 為什么需要定時(shí)同步

        在 Nacos 集群模式下,它作為一個(gè)完整的注冊(cè)中心,必須具有高可用特性。

        在集群模式下,客戶端只需要和其中一個(gè) Nacos 節(jié)點(diǎn)通信就可以了,但是每個(gè)節(jié)點(diǎn)其實(shí)是包含所有客戶端信息的,這樣做的好處是每個(gè) Nacos 節(jié)點(diǎn)只需要負(fù)責(zé)自己的客戶端就可以(分?jǐn)倝毫Γ?,而?dāng)客戶端想要拉取全量注冊(cè)表到本地時(shí),從任意節(jié)點(diǎn)都可以讀取到(數(shù)據(jù)一致性)。

        那么 Nacos 集群之間是如何通過(guò) Distro 協(xié)議來(lái)保持?jǐn)?shù)據(jù)一致性的呢?

        3.2 定期檢驗(yàn)元數(shù)據(jù)

        在版本 v1 中 ,采用的是定期檢驗(yàn)元信息的方式。元信息就是當(dāng)前節(jié)點(diǎn)包含的客戶端信息的 md5 值。

        檢驗(yàn)的原理如下圖所示:

        Nacos 各個(gè)節(jié)點(diǎn)會(huì)有一個(gè)心跳任務(wù),定期向其他機(jī)器發(fā)送一次數(shù)據(jù)檢驗(yàn)請(qǐng)求,在校驗(yàn)的過(guò)程中,當(dāng)某個(gè)節(jié)點(diǎn)發(fā)現(xiàn)其他機(jī)器上的數(shù)據(jù)的元信息和本地?cái)?shù)據(jù)的元信息不一致,則會(huì)發(fā)起一次全量拉取請(qǐng)求,將數(shù)據(jù)補(bǔ)齊。

        請(qǐng)求 URL:

        http://其他?Nacos?節(jié)點(diǎn)的?IP:port/nacos/v1/ns/distro/checksum?source?=?本機(jī)的IP地址:本機(jī)的端口號(hào)

        參數(shù):DistroData,內(nèi)部包裝的是一個(gè)Map<服務(wù)名稱,服務(wù)下實(shí)例的驗(yàn)證字符串 checksum>

        3.3 關(guān)于版本迭代的說(shuō)明

        在版本 ?v2 中,定期校驗(yàn)數(shù)據(jù)已經(jīng)不用了,采用的是健康檢查機(jī)制,來(lái)和其他節(jié)點(diǎn)來(lái)保持?jǐn)?shù)據(jù)的同步,由于涉及的內(nèi)容還挺多,放到下一講來(lái)專門講解 Nacos 的健康檢查機(jī)制:

        • 客戶端與 Nacos 節(jié)點(diǎn)的健康檢查機(jī)制。
        • 集群模式下的健康檢查機(jī)制。

        四、新節(jié)點(diǎn)同步機(jī)制,如何保持?jǐn)?shù)據(jù)一致性

        4.1 原理

        新加入的 Distro 節(jié)點(diǎn)會(huì)進(jìn)行全量數(shù)據(jù)拉取,輪詢所有的 Distro 節(jié)點(diǎn),向其他節(jié)點(diǎn)發(fā)送請(qǐng)求拉取全量數(shù)據(jù)。

        在全量拉取操作完成之后,每臺(tái)機(jī)器上都維護(hù)了當(dāng)前的所有注冊(cè)上來(lái)的非持久化實(shí)例數(shù)據(jù)。

        4.2 源碼分析

        DistroProtocol?類的構(gòu)造方法會(huì)啟動(dòng)一個(gè)同步任務(wù),從其他 Nacos 節(jié)點(diǎn)全量拉取非持久化實(shí)例數(shù)據(jù)。

        /nacos/core/distributed/distro/DistroProtocol.java
        ?startDistroTask()
        ;
        ??startLoadTask();
        /nacos/core/distributed/distro/task/load/DistroLoadDataTask.java
        ?run()
        ;
        ??load();
        ???loadAllDataSnapshotFromRemote();

        五、本地讀機(jī)制

        5.1 原理

        每個(gè) Nacos 節(jié)點(diǎn)雖然只負(fù)責(zé)屬于自己的客戶端,但是每個(gè)節(jié)點(diǎn)都是包含有所有的客戶端信息的,所以當(dāng)客戶端想要查詢注冊(cè)信息時(shí),可以直接從請(qǐng)求的 Nacos 的節(jié)點(diǎn)拿到全量數(shù)據(jù)。

        讀操作的原理

        這樣設(shè)計(jì)的好處是保證了高可用(AP),分為兩個(gè)方面:

        • ① 讀操作都能進(jìn)行及時(shí)的響應(yīng),不需要到其他節(jié)點(diǎn)拿數(shù)據(jù)。
        • ② 當(dāng)腦裂發(fā)生時(shí),Nacos 的節(jié)點(diǎn)也能正常返回?cái)?shù)據(jù),即使數(shù)據(jù)可能不一致,當(dāng)網(wǎng)絡(luò)恢復(fù)時(shí),通過(guò)健康檢查機(jī)制或數(shù)據(jù)檢驗(yàn)也能達(dá)到數(shù)據(jù)一致性。

        六、總結(jié)

        本篇通過(guò)原理圖 + 源碼的方式講解了 Distro 協(xié)議的原理,其中又分為幾個(gè)機(jī)制,而這幾個(gè)機(jī)制共同保證了 Nacos 的 AP。

        不足之處,本篇未針對(duì)源碼的設(shè)計(jì)進(jìn)行深入剖析,只是把主線捋出來(lái)了。如文中有問(wèn)題,歡迎探討~

        后續(xù):詳解 Nacos 的定期同步:心跳機(jī)制。

        SpringCloud 源碼剖析系列文章

        參考資料:

        Nacos 官網(wǎng)

        https://blog.csdn.net/qq_24768941/article/details/122420711

        https://www.cnblogs.com/lukama/p/14984858.html

        瀏覽 21
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            乱色综合 | 色视频国产 | 禁亲女爽浪骚乱h小喜 | 亚洲福利影院 | 十八禁网站免费观看 | 国产日韩精品无码去免费专区国产 | 男女爱爱网站 | freevideos性欧美 | 一道无码在线视 | 性生生交大片免费看1 |