Nacos架構
- Provider APP:服務提供者
- Consumer APP:服務消費者
- Name Server:通過VIP(Virtual IP)或DNS的方式實現Nacos高可用集群的服務路由
- Nacos Server:Nacos服務提供者,里面包含的Open API是功能訪問入口,Conig Service、Naming Service 是Nacos提供的配置服務、命名服務模塊。Consitency Protocol是一致性協議,用來實現Nacos集群節點的數據同步,這里使用的是Raft算法(Etcd、Redis哨兵選舉)
- Nacos Console:控制台
注冊中心的原理
- 服務實例在啟動時注冊到服務注冊表,並在關閉時注銷
- 服務消費者查詢服務注冊表,獲得可用實例
- 服務注冊中心需要調用服務實例的健康檢查API來驗證它是否能夠處理請求
SpringCloud完成注冊的時機
在Spring-Cloud-Common包中有一個類org.springframework.cloud. client.serviceregistry.ServiceRegistry ,它是Spring Cloud提供的服務注冊的標准。集成到Spring Cloud中實現服務注冊的組件,都會實現該接口。
該接口有一個實現類是NacoServiceRegistry。
SpringCloud集成Nacos的實現過程:
在spring-clou-commons包的META-INF/spring.factories中包含自動裝配的配置信息如下:
其中AutoServiceRegistrationAutoConfiguration就是服務注冊相關的配置類:
在AutoServiceRegistrationAutoConfiguration配置類中,可以看到注入了一個AutoServiceRegistration實例,該類的關系圖如下所示。
可以看出, AbstractAutoServiceRegistration抽象類實現了該接口,並且最重要的是NacosAutoServiceRegistration繼承了AbstractAutoServiceRegistration。
看到EventListener我們就應該知道,Nacos是通過Spring的事件機制繼承到SpringCloud中去的。
AbstractAutoServiceRegistration實現了onApplicationEvent抽象方法,並且監聽WebServerInitializedEvent事件(當Webserver初始化完成之后) , 調用this.bind ( event )方法。
最終會調用NacosServiceREgistry.register()方法進行服務注冊。
NacosServiceRegistry的實現
在NacosServiceRegistry.registry方法中,調用了Nacos Client SDK中的namingService.registerInstance完成服務的注冊。
跟蹤NacosNamingService的registerInstance()方法:
- 通過beatReactor.addBeatInfo()創建心跳信息實現健康檢測, Nacos Server必須要確保注冊的服務實例是健康的,而心跳檢測就是服務健康檢測的手段。
- serverProxy.registerService()實現服務注冊
心跳機制:
從上述代碼看,所謂心跳機制就是客戶端通過schedule定時向服務端發送一個數據包 ,然后啟動-個線程不斷檢測服務端的回應,如果在設定時間內沒有收到服務端的回應,則認為服務器出現了故障。Nacos服務端會根據客戶端的心跳包不斷更新服務的狀態。
注冊原理:
Nacos提供了SDK和Open API兩種形式來實現服務注冊。
Open API:
SDK:
這兩種形式本質都一樣,底層都是基於HTTP協議完成請求的。所以注冊服務就是發送一個HTTP請求:
對於nacos服務端,對外提供的服務接口請求地址為nacos/v1/ns/instance,實現代碼咋nacos-naming模塊下的InstanceController類中:
- 從請求參數匯總獲得serviceName(服務名)和namespaceId(命名空間Id)
- 調用registerInstance注冊實例
- 創建一個空服務(在Nacos控制台“服務列表”中展示的服務信息),實際上是初始化一個serviceMap,它是一個ConcurrentHashMap集合
- getService,從serviceMap中根據namespaceId和serviceName得到一個服務對象
- 調用addInstance添加服務實例
- 根據namespaceId、serviceName從緩存中獲取Service實例
- 如果Service實例為空,則創建並保存到緩存中
- 通過putService()方法將服務緩存到內存
- service.init()建立心跳機制
- consistencyService.listen實現數據一致性監聽
service.init ( ) 方法的如下圖所示,它主要通過定時任務不斷檢測當前服務下所有實例最后發送心跳包的時間。如果超時,則設置healthy為false表示服務不健康,並且發送服務變更事件。在這里請大家思考一一個問題,服務實例的最后心跳包更新時間是誰來觸發的?實際上前面有講到, Nacos客戶端注冊服務的同時也建立了心跳機制。
putService方法,它的功能是將Service保存到serviceMap中:
繼續調用addInstance方法把當前注冊的服務實例保存到Service中:
總結:
- Nacos客戶端通過Open API的形式發送服務注冊請求
- Nacos服務端收到請求后,做以下三件事:
- 構建一個Service對象保存到ConcurrentHashMap集合中
- 使用定時任務對當前服務下的所有實例建立心跳檢測機制
- 基於數據一致性協議服務數據進行同步
- 服務提供者地址查詢
Open API:
SDK:
InstanceController中的list方法:
- 解析請求參數
- 通過doSrvIPXT返回服務列表數據
- 根據namespaceId、serviceName獲得Service實例
- 從Service實例中基於srvIPs得到所有服務提供者實例
- 遍歷組裝JSON字符串並返回
Nacos服務地址動態感知原理
可以通過subscribe方法來實現監聽,其中serviceName表示服務名、EventListener表示監聽到的事件:
具體調用方式如下:
或者調用selectInstance方法,如果將subscribe屬性設置為true,會自動注冊監聽:
Nacos客戶端中有一個HostReactor類,它的功能是實現服務的動態更新,基本原理是:
- 客戶端發起時間訂閱后,在HostReactor中有一個UpdateTask線程,每10s發送一次Pull請求,獲得服務端最新的地址列表
- 對於服務端,它和服務提供者的實例之間維持了心跳檢測,一旦服務提供者出現異常,則會發送一個Push消息給Nacos客戶端,也就是服務端消費者
- 服務消費者收到請求之后,使用HostReactor中提供的processServiceJSON解析消息,並更新本地服務地址