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
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 APIAPI汇总如下:
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 |
返回示例:ok
注销实例
key | 含义 | 备注 |
namespaceId | 命名空间 | 默认为public |
ip | 实例IP地址 |
|
port | 实例端口 |
|
serviceName | 服务名 |
|
clusterName | 集群名 | 默认为DEFAULT |
返回示例:ok
获取实例列表
key | 含义 | 备注 |
namespaceId | 命名空间 | 默认为public |
serviceName | 服务名 |
|
clusters | 集群名 | 默认为DEFAULT |
udpPort | 监听的UPD端口号 | 由PushReceiver创建 |
clientIP | 客户端IP |
|
healthyOnly | 是否只返回健康的实例 |
返回示例:{“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用来标识状态的 |
返回示例:{“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 | 过滤器 |
返回示例:{“count”:1,”doms”:[“nacos.test.3”]}
结语Nacos服务发现的客户端较为简单,其他语言也可以参照其API来实现客户端。如果对源码实现感兴趣,可以自己看下代码。