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的几个核心类及其关系如下图。我们分别来看一下这几个类。
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 |
返回示例: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来实现客户端。如果对源码实现感兴趣,可以自己看下代码。
Nacos 解读:服务发现客户端的更多相关文章
- 聊一聊Yarp结合Nacos完成服务发现
背景 Yarp 这个反向代理出来后,相信还是有不少人在关注的. 在 Yarp 中,反向代理的配置默认也是基于配置文件的,也有不少大佬已经把这个配置做成了数据库配置+可视化界面. 仔细想了想,做成数据库 ...
- Docker安装Nacos动态服务发现、配置和服务管理平台
一.通过DockerHub拉镜像,版本查看:https://github.com/nacos-group/nacos-docker //稳定版,有权限 docker pull nacos/nacos- ...
- spring-boot 2.5.4,nacos 作为配置、服务发现中心,Cloud Native Buildpacks 打包镜像,GitLab CI/CD
spring-boot 2.5.4,nacos 作为配置.服务发现中心,Cloud Native Buildpacks 打包镜像,GitLab CI/CD 本文主要介绍 Java 通过 Cloud N ...
- 配置中心Nacos(服务发现)
服务演变之路 单体应用架构 在刚开始的时候,企业的用户量.数据量规模都⽐较⼩,项⽬所有的功能模块都放在⼀个⼯程中编码.编译.打包并且部署在⼀个Tomcat容器中的架构模式就是单体应用架构,这样的架构既 ...
- SpringBoot使用Nacos服务发现
本文介绍SpringBoot应用使用Nacos服务发现. 上一篇文章介绍了SpringBoot使用Nacos做配置中心,本文介绍SpringBoot使用Nacos做服务发现. 1.Eureka闭源 相 ...
- ASP.NET Core结合Nacos来完成配置管理和服务发现
目录 前言 Nacos的简介 启动Nacos 配置管理 服务发现 写在最后 前言 今年4月份的时候,和平台组的同事一起调研了一下Nacos,也就在那个时候写了.net core版本的非官方版的SDK. ...
- .net5+nacos+ocelot 配置中心和服务发现实现
最近一段时间 因公司业务需要,需要使用.net5做一套微服务的接口,使用nacos 做注册中心和配置中心,ocelot做网关. 因为ocelot 支持的是consol和eureka,如果使用nacos ...
- 【笔记】 springCloud--Alibaba--服务注册和服务发现
接着上一次的nacos初步讲解和安装 任意门:https://www.cnblogs.com/Yangbuyi/p/13479767.html 如果启动失败的话 上一篇也是讲解过的. 本文章开始服务注 ...
- Consul 服务发现和配置
Service discovery and configuration made easy. Distributed, highly available, and datacenter-aware. ...
随机推荐
- POJ3662Telephone Lines(最短路+二分)
传送门 题目大意:n个点p条边,每条边有权值,让1和n点联通,可以将联通1--n的边选k条免费, 求剩下边权的最大值. 题解:二分一个答案x,大于x的边权设为1,小于等于x的边权设为0,跑最短路. 若 ...
- vue中mode hash 和 history的区别
对于 Vue 这类渐进式前端开发框架,为了构建 SPA(单页面应用),需要引入前端路由系统,这也就是 Vue-Router 存在的意义.前端路由的核心,就在于 —— 改变视图的同时不会向后端发出请求. ...
- 大话设计模式Python实现-工厂方法模式
工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延时到其子类. #!/usr/bin/env python ...
- C# 中如何创建异步平行任务?
解释都在代码里,直接贴代码了: private async void btnStartRequestResource_Click(object sender, EventArgs e) { ShowA ...
- DAX 第四篇:CALCULATE详解
CALCULATE()函数是DAX中最复杂的函数,用于计算由指定过滤器修改的上下文中的表达式. CALCULATE(<expression>,<filter1>,<fil ...
- 【03】Nginx:location / root / alias
写在前面的话 前面我们谈了 nginx 基础的 WEB 服务配置以及定制我们的日志显示格式,接下来我能更加详细的说说 server 字段. location 字段 在 Server 中,如果我们只是一 ...
- C# 委托补充01
上一篇文章写了委托的最基本的一些东西,本篇咱们扯扯委托其他的东西. 示例1插件编程 根据对委托的理解,委托可以把一个方法当作参数进行传递,利用这个特性我们可以使用委托,实现插件编程. public d ...
- DLT645 1997 协议解析
源码下载 -> 提取码 QQ:505645074 DLT645.zip 工具 源码 规约解析 DL/T645-07: 数据帧格式: 注意事项: (1)前导字节-一般在传输帧信息前,都要有0~4 ...
- Retrofit的优点
Retrofit的优点 可以配置不同HTTP client来实现网络请求,如okhttp.httpclient等 将接口的定义与使用分离开来,实现结构. 支持多种返回数据解析的Converter可以快 ...
- php 使用fsockopen 发送http请求
需求背景 在公司开发这么一个需求,每天三次定时催付待客服催付状态的订单,设定每天15.16.17点三次执行job任务来给一批订单打电话催付,需要三个时间点都把待客服催付的订单拨打一遍电话,根据数据组统 ...