转载

Nacos解读:服务发现客户端

Nacos 是阿里巴巴的微服务开源项目,用于服务发现和配置管理,开源以来我就一直关注,在此准备以几篇文章来窥其全貌,但大段大段贴代码就没必要了,这里用自己的一些理解和总结来帮助大家理解。文章将基于截止目前最新发布的0.8版本,Nacos的使用方式参考官方文档即可,这里主要从原理和实现上来讲。

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的几个核心类及其关系如下图。我们分别来看一下这几个类。

Nacos解读:服务发现客户端

core-class

NacosNamingService

NacosNamingServiceNamingService 接口的实现类。实现了上面提到的那些方法。

此外, NacosNamingService 还起到了初始化其他核心类的作用,因为对外提供的方法都是委托给其他核心类处理的。按顺序将依次初始化 EventDispatcherNamingProxyBeatReactorHostReactor

NacosNamingService 的构造函数我们也可以了解到,可以进行一些参数的自定义,总结如下(部分概念的含义可参考 官方文档 ):

key 含义 默认值
namespace 命名空间 public
com.alibaba.nacos.naming
.log.filename
日志文件名 naming.log
com.alibaba.nacos.naming
.log.level
日志级别 INFO
com.alibaba.nacos.naming
.cache.dir
缓存目录(缓存服务信息) {user.home}/nacos/
naming/{namespace}
serverAddr Nacos服务端地址
endpoint 接入点
namingLoadCacheAtStart 初始化时是否从缓存读取服务信息 false
namingClientBeatThreadCount 心跳线程池线程数 1~CPU核心数的一半
namingPollingThreadCount 服务更新线程池线程数 1~CPU核心数的一半

EventDispatcher

EventDispatcher 与其他事件分发的组件没什么不同,用于处理 subscribeunsubscribe 等等与服务监听相关的方法,并分发 NamingEvent 到各Listener。

成员变量 ConcurrentMap<String, List<EventListener>> observerMap 保存了注册的Listener,key为 {服务名}@@{集群名} ,value为各个 EventListener 的列表。

EventDispatcher 会启动 1 个名为 com.alibaba.nacos.naming.client.listener 的线程用于处理事件的分发。

注意点:

  • 分发 NamingEvent 时,按照 subscribe(...) 方法的调用顺序串行依次调用 EventListeneronEvent(...) 方法。
  • 调用 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来实现客户端。如果对源码实现感兴趣,可以自己看下代码。

原文  https://blog.darkness463.top/2019/02/20/Nacos-Naming-Client/
正文到此结束
Loading...