Nacos可以分為服務發現(Naming)和配置管理(Config)兩塊,而從使用上來說,又可分為Nacos服務端和客戶端,第一篇先來聊下服務發現(Naming)的客戶端。

Example

我們從官方示例入手。

Properties properties = new Properties();
properties.setProperty("serverAddr", System.getProperty("serverAddr"));
properties.setProperty("namespace", System.getProperty("namespace"));

NamingService naming = NamingFactory.createNamingService(properties);

naming.registerInstance("nacos.test.3", "11.11.11.11", 8888, "TEST1");

naming.registerInstance("nacos.test.3", "2.2.2.2", 9999, "DEFAULT");

System.out.println(naming.getAllInstances("nacos.test.3"));

naming.deregisterInstance("nacos.test.3", "2.2.2.2", 9999, "DEFAULT");

System.out.println(naming.getAllInstances("nacos.test.3"));

naming.subscribe("nacos.test.3", new EventListener() {
    @Override
    public void onEvent(Event event) {
        System.out.println(((NamingEvent)event).getServiceName());
        System.out.println(((NamingEvent)event).getInstances());
    }
});

NamingService

從官方示例可以了解到,對於我們使用者來說,NamingService是Nacos對外提供給使用者的接口,其實現類為com.alibaba.nacos.client.naming.NacosNamingService,歸納起來,NamingService提供了以下方法:

  • registerInstance:注冊實例。
  • deregisterInstance:注銷實例。
  • getAllInstances:獲取某一服務的所有實例。
  • selectInstances:獲取某一服務健康或不健康的實例。
  • selectOneHealthyInstance:根據權重選擇一個健康的實例。
  • getServerStatus:檢測服務端健康狀態。
  • subscribe:注冊對某個服務的監聽。
  • unsubscribe:注銷對某個服務的監聽。
  • getSubscribeServices:獲取被監聽的服務。
  • getServicesOfServer:獲取命名空間(namespace)下的所有服務名。【注:此方法有個小坑,參數pageNo要從1開始】

核心類

Naming Client的幾個核心類及其關系如下圖。我們分別來看一下這幾個類。

 

core-class

core-class

NacosNamingService

NacosNamingService是NamingService接口的實現類。實現了上面提到的那些方法。
此外,NacosNamingService還起到了初始化其他核心類的作用,因為對外提供的方法都是委托給其他核心類處理的。按順序將依次初始化EventDispatcher、NamingProxy、BeatReactor、HostReactor。
從NacosNamingService的構造函數我們也可以了解到,可以進行一些參數的自定義,總結如下(部分概念的含義可參考官方文檔):

EventDispatcher

EventDispatcher與其他事件分發的組件沒什么不同,用於處理subscribe、unsubscribe等等與服務監聽相關的方法,並分發NamingEvent到各Listener。
成員變量ConcurrentMap<String, List<EventListener>> observerMap保存了注冊的Listener,key為{服務名}@@{集群名},value為各個EventListener的列表。
EventDispatcher會啟動1個名為com.alibaba.nacos.naming.client.listener的線程用於處理事件的分發。

注意點:

  • 分發NamingEvent時,按照subscribe(…)方法的調用順序串行依次調用EventListener的onEvent(…)方法。
  • 調用subscribe(…)方法會引起對應Service的事件分發。

NamingProxy

NamingProxy用於與Nacos服務端通信,注冊服務、注銷服務、發送心跳等都經由NamingProxy來請求服務端。
NamingProxy會啟動1個名為com.alibaba.nacos.client.naming.serverlist.updater的線程,用於定期調用refreshSrvIfNeed()方法更新Nacos服務端地址,默認間隔為30秒
對服務端API的調用將在后文總結。

注意點:refreshSrvIfNeed()方法對Nacos服務端地址的更新僅在使用endpoint的時候才會進行實際更新,如果是通過serverAddr配置的Nacos服務端地址,refreshSrvIfNeed()方法將不會進行任何操作。

BeatReactor

BeatReactor用於向Nacos服務端發送已注冊服務的心跳。
成員變量Map<String, BeatInfo> dom2Beat中保存了需要發送的BeatInfo,key為{serviceName}#{ip}#{port},value為對應的BeatInfo。
BeatReactor會啟動名為com.alibaba.nacos.naming.beat.sender的線程來發送心跳,默認線程數為1~CPU核心數的一半,可由namingClientBeatThreadCount參數指定。
默認情況下每5秒發送一次心跳,可根據Nacos服務端返回的clientBeatInterval的值調整心跳間隔。

注意點:0.8版本有一個小bug,客戶端心跳間隔並不受服務端返回值的控制。我已提交PR,預計將在0.9版本修復。

HostReactor

HostReactor用於獲取、保存、更新各Service實例信息。
成員變量Map<String, ServiceInfo> serviceInfoMap中保存了已獲取到的服務的信息,key為{服務名}@@{集群名}。
HostReactor會啟動名為com.alibaba.nacos.client.naming.updater的線程來更新服務信息,默認線程數為1~CPU核心數的一半,可由namingPollingThreadCount參數指定。定時任務UpdateTask會根據服務的cacheMillis值定時更新服務信息,默認值為10秒。該定時任務會在獲取某一服務信息時創建,保存在成員變量Map<String, ScheduledFuture<?>> futureMap中。

其他

PushReceiver

PushReceiver用於接收Nacos服務端的推送,初始化時會創建DatagramSocket使用UDP的方式接收推送。會啟動1個名為com.alibaba.nacos.naming.push.receiver的線程。

FailoverReactor

用於故障轉移,會啟動1個名為com.alibaba.nacos.naming.failover的線程並定時讀取名為00-00—000-VIPSRV_FAILOVER_SWITCH-000—00-00的文件,內容為1時表示開啟,此時獲取服務信息時會返回FailoverReactor緩存的服務信息。

Balancer

根據服務實例的權重挑選一個實例,實現簡單的負載均衡。

DiskCache

用於服務信息的持久化。

Naming API

API匯總如下:

Method URI 含義
POST /nacos/v1/ns/instance 注冊實例
DELETE /nacos/v1/ns/instance 注銷實例
GET /nacos/v1/ns/instance/list 獲取實例列表
PUT /nacos/v1/ns/instance/beat 發送心跳
GET /nacos/v1/ns/api/hello Nacos服務端狀態
GET /nacos/v1/ns/service/list 獲取所有服務名

參數列表及示例

注冊實例

key 含義 備注
namespaceId 命名空間 默認為public
ip 實例IP地址  
port 實例端口  
weight 權重 默認為1.0
enable 是否開啟 默認為true
healthy 健康狀態 默認為true
metadata 其他信息  
serviceName 服務名  
clusterName 集群名 默認為DEFAULT

請求示例:http://localhost:8848/nacos/v1/ns/instance?metadata=%7B%7D&namespaceId=public&port=8888&enable=true&healthy=true&ip=11.11.11.11&clusterName=TEST1&weight=1.0&serviceName=nacos.test.3&encoding=UTF-8&

返回示例:ok

注銷實例

key 含義 備注
namespaceId 命名空間 默認為public
ip 實例IP地址  
port 實例端口  
serviceName 服務名  
clusterName 集群名 默認為DEFAULT

請求示例:http://localhost:8848/nacos/v1/ns/instance?cluster=DEFAULT&serviceName=nacos.test.3&encoding=UTF-8&namespaceId=public&port=9999&ip=2.2.2.2&

返回示例:ok

獲取實例列表

key 含義 備注
namespaceId 命名空間 默認為public
serviceName 服務名  
clusters 集群名 默認為DEFAULT
udpPort 監聽的UPD端口號 由PushReceiver創建
clientIP 客戶端IP  
healthyOnly 是否只返回健康的實例

請求示例:http://localhost:8848/nacos/v1/ns/instance/list?healthyOnly=false&namespaceId=public&clientIP=172.16.20.114&serviceName=nacos.test.3&udpPort=53957&encoding=UTF-8&

返回示例:{“metadata”:{},”dom”:”nacos.test.3”,”cacheMillis”:10000,”useSpecifiedURL”:false,”hosts”:[{“valid”:true,”marked”:false,”metadata”:{},”instanceId”:”2.2.2.2#9999#DEFAULT#nacos.test.3”,”port”:9999,”ip”:”2.2.2.2”,”clusterName”:”DEFAULT”,”weight”:1.0,”serviceName”:”nacos.test.3”,”enabled”:true},{“valid”:true,”marked”:false,”metadata”:{},”instanceId”:”11.11.11.11#8888#TEST1#nacos.test.3”,”port”:8888,”ip”:”11.11.11.11”,”clusterName”:”TEST1”,”weight”:1.0,”serviceName”:”nacos.test.3”,”enabled”:true}],”checksum”:”bd1054e6afb8d10730d945d74c4ce4421550584589236”,”lastRefTime”:1550584589236,”env”:””,”clusters”:””}

發送心跳

key 含義 備注
namespaceId 命名空間 默認為public
serviceName 服務名  
beat BeatInfo的JSON字符串

BeatInfo對象結構如下,與Instance對象類似:

field 含義 備注
port 端口  
ip IP地址  
weight 權重  
metadata 其他信息  
serviceName 服務名  
clusterName 集群名  
scheduled 是否心跳中 這個是BeatReactor用來標識狀態的

請求示例:http://localhost:8848/nacos/v1/ns/instance/beat?beat=%7B%22cluster%22%3A%22DEFAULT%22%2C%22ip%22%3A%222.2.2.2%22%2C%22metadata%22%3A%7B%7D%2C%22port%22%3A9999%2C%22scheduled%22%3Atrue%2C%22serviceName%22%3A%22nacos.test.3%22%2C%22weight%22%3A1.0%7D&serviceName=nacos.test.3&encoding=UTF-8&namespaceId=public&

返回示例:{“clientBeatInterval”:5000}

Nacos服務端狀態

key 含義 備注
namespaceId 命名空間 默認為public

請求示例:http://localhost:8848/nacos/v1/ns/api/hello?encoding=UTF-8&namespaceId=public&

返回示例:{“msg”:”Hello! I am Nacos-Naming and healthy! total services: raft 2, local port:8848”}

獲取所有服務名

key 含義 備注
namespaceId 命名空間 默認為public
pageNo 頁碼 注意從1開始
pageSize 返回數量  
selector 過濾器

請求示例:http://localhost:8848/nacos/v1/ns/service/list?pageSize=100&encoding=UTF-8&namespaceId=public&pageNo=0&

返回示例:{“count”:1,”doms”:[“nacos.test.3”]}

結語

Nacos服務發現的客戶端較為簡單,其他語言也可以參照其API來實現客戶端。如果對源碼實現感興趣,可以自己看下代碼。