本文已收錄 https://github.com/lkxiaolou/lkxiaolou 歡迎star。
前言
在之前的文章中分析了Nacos配置中心,配置中心的核心是配置的建立、讀取、推送。
注冊中心的核心比配置中心多一個
服務探活
子產品,他倆的相似度非常高,甚至阿裡内部的注冊中心就叫
ConfigServer
。
Nacos注冊中心打算分成幾個子產品來分析,本文重點在于
概要設計
,基于2.0.0版本。
環境搭建
用Nacos的源碼來搭建源碼閱讀和調試環境,可參考《Nacos配置中心子產品詳解》
Nacos調試環境搭建
部分。
其中 JVM參數可以指定隻啟動Naming子產品,也可以不指定,預設全都啟動。
example子產品下将NamingExample複制一份進行測試。
設計概要
服務發現模型
用戶端視角的服務發現模型(注意:服務端視角的模型定義與用戶端視角有差別)包含以下幾點内容:
- Service:服務
- Cluster:叢集
- Instance:執行個體
代碼注釋:We introduce a 'service --> cluster --> instance' model, in which service stores a list of clusters, which contains a list of instances
他們的關系如下

Service
- name:服務名
- protectThreshold:保護門檻值,限制了執行個體被探活摘除的最大比例
- appName:服務的應用名,暫無實際用處
- groupName:分組名
- metadata:中繼資料
Cluster
- serviceName:所屬服務名
- name:叢集名
- healthChecker:服務探活配置,此處僅對服務端主動探活生效,有TCP、HTTP、MySQL、None幾種方式,預設TCP
- defaultPort:預設端口
- defaultCheckPort:預設探活端口
- useIPPort4Check:是否使用port進行探活
Instance
- instanceId:執行個體id,唯一标志,Nacos提供了
和simple
兩種算法來生成,預設是snowflake
,其生成方式為simple
ip#port#clusterName#serviceName
- ip:執行個體ip
- port:執行個體port
- weight:執行個體權重
- healthy:執行個體健康狀态
- clusterName:所屬叢集名
- enabled:是否接收請求,可用于臨時禁用或摘流等場景
- ephemeral:是否為臨時執行個體,後文會介紹該參數
- getInstanceHeartBeatInterval:擷取執行個體心跳上報間隔時間,預設5秒,可配置
- getInstanceHeartBeatTimeOut:擷取心跳逾時時間,15秒,配置
- getIpDeleteTimeout:擷取ip被删除的逾時時間,預設30秒,可配置
- getInstanceIdGenerator:擷取id生成器
除了上述的三層模型外,Nacos注冊中心和配置中心有着一樣的namespace設計,與client綁定,可隔離環境,租戶。
接口設計
- registerInstance:注冊執行個體
- deregisterInstance:登出執行個體
- getAllInstances:擷取一個服務的所有執行個體(包括不健康)
- selectInstances:根據條件擷取一個服務的執行個體
- selectOneHealthyInstance:根據負載均衡政策擷取服務的一個健康的執行個體
- subscribe:訂閱服務
- unsubscribe:取消訂閱服務
- getServicesOfServer:根據條件分頁擷取所有服務
互動流程
Nacos 2.0 為ephemeral不同的執行個體提供了兩套流程:
- ephemeral=false,永久執行個體,與server端的互動采用http請求,server節點間資料同步采用了raft協定,健康檢查采用了server端主動探活的機制
- ephemeral=true,臨時執行個體,與server端的互動采用grpc請求,server節點間資料同步采用了distro協定,健康檢查采用了TCP連接配接的KeepAlive模式
臨時執行個體的互動流程
- client初始化,與server建立連接配接
- 隻與其中一台server節點建立長連接配接
- client 注冊服務,将serviceName+ip+port+clusterName等資料打包發送grpc請求
- 同時用戶端緩存已注冊過的服務,當client與server連接配接斷開重連時,client重新将這些資料注冊到server端
- server端接收到client的注冊請求,将注冊資訊存入client對象(用于儲存client的所有資料)中,并觸發ClientChangedEvent、ClientRegisterServiceEvent、InstanceMetadataEvent
- ClientChangedEvent觸發server節點之間的資料同步(distro協定)
- ClientRegisterServiceEvent觸發更新publisherIndexes(儲存service => clientId的Map<Service, Set>,即哪些用戶端注冊了這個服務的索引),同時也觸發一個ServiceChangedEvent,該事件負責向監聽該服務的用戶端進行推送
- InstanceMetadataEvent,進行中繼資料,Nacos在2.0中将中繼資料與基礎資料拆分開,分為不同的處理流程
- client訂閱服務
- 根據serviceName、groupName、clusters資訊生成key,建立eventListener,同時向server端發送訂閱請求,并緩存訂閱資訊,用于連接配接斷開重連後再次向server端發送資訊
- server端接收到client的訂閱請求
- 将訂閱資訊打包為subscribers,并存入client對象中,觸發ClientSubscribeServiceEvent事件
- ClientSubscribeServiceEvent事件更新subscriberIndexes(儲存service => clientId的Map<Service, Set>,即哪些用戶端訂閱了這個服務的索引),同時觸發ServiceSubscribedEvent事件
- ServiceSubscribedEvent事件會延時500ms向該client推送該服務的最新資料
- 反向的操作如登出、取消訂閱與正向操作類似,不再贅述
最後
本文從總體上分析了Nacos 2.0的模型設計、接口設計以及互動流程,讀完後對Nacos的服務發現有一個整體上的認識。
後續篇幅會從細節入手,如dubbo Nacos擴充、一緻性協定、探活、CMDB擴充等逐一進行分析。
搜尋關注微信公衆号"捉蟲大師",後端技術分享,架構設計、性能優化、源碼閱讀、問題排查、踩坑實踐。