文章目錄
- 概述
- 一、客戶端新注冊實例信息在集群間同步
- 二、服務端集群節點信息在集群間同步
- 2.1、DistroMapper
- 2.2、ProtocolManager
- 2.3、ServerListManager
- 2.4、RaftPeerSet
- 三、客戶端實例狀態信息在集群間同步
- 四、服務端新節點上線同步集群數據
概述
??在Nacos集群模式下,客戶端可以選擇注冊在集群中的某一臺機器上:
server:port: 9002
spring:application:name: stock-servicecloud:nacos:discovery:# 指定nacos server的地址server-addr: localhost:8847# 指定集群名稱cluster-name: njmetadata:version: v3#ephemeral: falsenamespace: public
??當客戶端啟動完成后,集群中8847
所在的服務端,首先獲取到了客戶端實例,隨后8867
,8857
也同步獲取到了客戶端實例 。
??這里就牽涉到兩個問題:
- 客戶端只向集群中的某一臺機器進行了注冊,集群中的其他節點是如何感知到的?
- 如果客戶端下線,或者健康狀態發生改變,其他節點又是如何同步的?
??也就是文中的四點:
- 客戶端啟動,注冊實例到集群中的某個節點,該注冊信息在整個集群中的同步。
- 客戶端實例狀態信息發生改變,在集群間同步。
- 服務端集群節點狀態信息在集群間同步。
- 服務端新上線的節點,同步集群中的節點信息。
一、客戶端新注冊實例信息在集群間同步
??當客戶端發起注冊實例的請求,到達服務端后,在DistroConsistencyServiceImpl#put
方法中,除了單機模式下將實例信息放入阻塞隊列之外,集群模式
下還需要進行同步。默認的延遲時間是1s。
??在sync
方法中,首先會去創建一個DistroDelayTask
任務實例,然后放入NacosDelayTaskExecuteEngine
中。該類是Nacos自己實現的延遲任務執行引擎。這里的套路就和注冊實例信息一樣,首先將實例放入隊列中,然后再由其他線程異步獲取并執行邏輯。
??最終調用到的是DistroSyncChangeTask#run
方法,同樣是發起http請求,通知集群中的其他節點,如果返回失敗,還會進行重試:
??訪問的是服務端的/distro/datum
:
??請求發送到服務端的DistroController
的onSyncDatum
??最終執行onPut
方法,將實例信息放入隊列中。
二、服務端集群節點信息在集群間同步
??如果集群中的某個節點宕機了,或者集群啟動,那么集群之間需要同步狀態信息,是通過registerServerStatusReporter
定時任務實現的,默認2s一次。
??真正執行邏輯的是ServerStatusReporter
的run
方法中的synchronizer.send
:
??同樣是發送http請求,通知其他節點,調用的是OperatorController
的/server/status
接口。
??為了觀察集群中某個節點下線后,其他節點是如何同步狀態的,手動模擬某個節點下線:
??在onReceiveServerStatus
方法中,首先會對傳入的configInfo
參數進行一系列的處理,獲取IP端口,權重等信息,然后調用 memberManager.update(server);
方法。
??update
方法,如果當前節點的狀態已經是DOWN
,就會直接從memberAddressInfos
中刪除該實例信息:
??并且在實例信息有改變的情況下,還會通過notifyMemberChange();
方法去發布一個事件:
??實際上該事件是放在DefaultPublisher
的queue
屬性中的,該屬性是一個阻塞隊列。
??真正處理該事件的方法,是DefaultPublisher
的run方法:
??receiveEvent
又調用了notifySubscriber
,這里的subscribers
訂閱者有四個,這四個訂閱者都是MemberChangeListener
的子類
2.1、DistroMapper
??DistroMapper
的onEvent
,主要的用途是用于Distro一致性協議中的數據同步路由。在該方法中,主要完成了三件事:
- 選出健康節點,并生成節點 IP 列表。
- 排序節點列表,確保順序一致。
- 替換舊的健康節點列表。
??當 Nacos 集群成員發生變化時,DistroMapper 會自動更新健康節點列表,并保持節點順序一致,保障 AP 模式下的數據同步路由穩定運行。
2.2、ProtocolManager
??ProtocolManager
的onEvent
用于當集群成員列表發生變化時,分別通知 AP 協議組件(Distro)和 CP 協議組件(Raft),以更新它們各自的一致性成員信息,啟用不同的線程進行處理。ProtocolManager
是這兩種協議的統一協調者。
2.3、ServerListManager
??ServerListManager
的onEvent
用于接收到成員列表變更事件后,直接用最新的成員列表替換本地的servers列表。ServerListManager
是用于維護當前 Nacos 節點所感知的集群服務器列表的,會被其他組件依賴:
- 服務注冊、發現時選擇服務器;
- 啟動時檢查集群節點是否就緒;
- 分布式協議中做心跳/同步時依據節點列表操作
2.4、RaftPeerSet
??RaftPeerSet
的onEvent
用于Naocs CP模式下的raft算法,在該方法中,主要完成了:
- 獲取當前的最新成員列表, 和上一次的成員列表比較,找出新增或變更的節點。
- 如果成員發生了變化(比如新節點加入或節點 IP 變化),通知 Raft 協議層更新 投票成員列表
- 更新緩存,記錄最新的集群節點狀態
三、客戶端實例狀態信息在集群間同步
??這里利用到的是scheduleServiceReporter
定時任務。默認一分鐘執行一次。
??真正執行邏輯的是ServiceReporter
的run
方法:
??distroMapper.responsible(serviceName)是關鍵代碼,該方法主要用于判斷當前節點是否負責處理某個服務(serviceName) 的數據同步和持久化
- 獲取當前 Nacos 集群中所有 健康的節點地址。
- 如果 Distro 沒啟用,或者是單機模式,直接返回 true,所有服務都由自己負責。
- 健康節點列表為空,表示 Distro 還沒初始化好,此時不能確定責任關系。
- 獲取當前節點在健康節點列表中的索引。
- 用服務名做 Hash,然后和健康節點數量取模,得到這個服務應由哪個節點處理(索引)。
- 判斷這個 target 索引,是否屬于當前節點。
??同樣是發送http請求,通知其他節點,調用的是ServiceController
的serviceStatus
方法,主要用于驗證當前服務節點的服務信息是否和其他節點保持一致。
??在ServiceController
的serviceStatus
方法中,如果發現服務狀態有變化,則會調用addUpdatedServiceToQueue
方法,向阻塞隊列中存入信息:
??UpdatedServiceProcessor
的run方法,再次開啟任務:
??所以最終執行邏輯的是ServiceUpdater
的run
:
??實例的健康狀態發生了改變:
??發布事件,被實現了ApplicationListener<ServiceChangeEvent>
的實現類監聽,最終通過udp協議,推送給客戶端。
??在集群模式下,服務端檢查心跳的定時任務,實際上也是由集群中的某一個節點去完成的:
??這里就是利用了上面提到的hash算法,不滿足條件的節點直接return,不會執行心跳檢查任務。
四、服務端新節點上線同步集群數據
??上線的新節點,需要從其他節點同步數據,是通過DistroLoadDataTask
任務實現的:
??在load方法中:
- 首先獲取集群中除了自己的其他節點。如果沒有獲取到,就會阻塞等待。
- 如果還沒有注冊任何數據類型,同樣阻塞等待。
- 從其他 Nacos 節點獲取數據快照;
??然后循環調用loadAllDataSnapshotFromRemote
方法,該方法有兩個關鍵操作:
- 根據地址調用
DistroController
的/distro/datums
接口,獲取數據。 - 將數據寫入內存中。