Nacos 注册中心的特性

我们以现实业务为例,某超市会员线上购物送等额积分,此积分在下次购物时可抵用现金,其中涉及订单服务、会员服务、积分服务等多个微服务模块。
在以往单实例情况下,服务间通常采用点对点通信,即采用 IP+端口+接口的形式直接调用。但考虑避免单点负载压力过大以及高可用的性能要求,通常会部署多实例节点保障系统的性能,但增加多实例后,调用方该如何选择哪个服务提供者进行处理呢?还有当服务提供者出现故障后,如何将后续请求转移到其他可用实例上呢?面对这些问题,微服务架构必须要引入注册中心对所有服务实例统一注册管理、有组织地进行健康检查来保障服务的可用性。
image.png
所有服务实例向注册中心登记
在 Spring Cloud Alibaba 生态中,由 Nacos 中间件承担注册中心职责,需要独立部署。下面我们先来认识一下 Nacos。
Nacos 官方地址为https://nacos.io/zh-cn/index.html。由阿里开源,官方定义为:
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

Nacos 官方介绍
Nacos 具备以下职能:

  • 服务发现及管理;
  • 动态配置服务;
  • 动态 DNS 服务。

下图是Nacos 的核心特征:
image.png
Nacos 的核心特性
因为官网有完整的功能介绍,这里不再赘述。

Nacos领域模型

Nacos概念 - 图3
Nacos概念 - 图4

(一)NameSpace

NameSpace(默认的NameSpace是”public“ NameSpace可以进行资源隔离,比如我们dev环境下的NameSpace下的服务是调用不到prod的NameSpace下的微服务)
NameSpace隔离级别是一套服务,比如说是生产环境,线上环境等等.不同的环境相互隔离.

(二)Group

Group也是分组的,如果小组不同也是不能调用通的,Group可以避免微服务之间不相关的微服务进行相互调用,比如说库存服务和物流服务有关联,可以相互调用,就分为一个Group,订单和物流服务没有关联,不能相互调用,就可以分为不同的Group,这样能进行不相关的服务的隔离.

(三)Cluster


比如说订单服务我要部署两套,一套是南京机房的订单服务,一套是北京机房的订单服务,要做到避免跨集群调用,就是说北京机房的订单服务尽可以去调用北京机房的支付服务,这就是避免跨集群调用.

因为北京调用南京的话,可能会出现网络延迟问题等等.

Nacos核心功能点介绍

(一)核心功能点


服务注册:Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务,提供自身的元数据,比如ip地址、端口等信息。
Nacos Server接收到注册请求后,就会把这些元数据信息存储在一个双层的内存Map中。

服务心跳:在服务注册后,Nacos Client会维护一个定时心跳来持续通知Nacos Server,说明服务一直处于可用状态,防止被剔除。默认
5s发送一次心跳。

服务同步:Nacos Server集群之间会互相同步服务实例,用来保证服务信息的一致性。

服务发现:服务消费者(Nacos Client)在调用服务提供者的服务时,会发送一个REST请求给Nacos Server,获取上面注册的服务清
单,并且缓存在Nacos Client本地,同时会在Nacos Client本地开启一个定时任务定时拉取服务端最新的注册表信息更新到本地缓存

服务健康检查:Nacos Server会开启一个定时任务用来检查注册服务实例的健康情况,对于超过15s没有收到客户端心跳的实例会将它的
healthy属性置为false(客户端服务发现时不会发现),如果某个实例超过30秒没有收到心跳,直接剔除该实例(被剔除的实例如果恢复发送
心跳则会重新注册)

(二)Nacos架构图


Nacos概念 - 图5

Nacos 注册中心的心跳机制

下图阐述了微服务与 Nacos 服务器之间的通信过程。在微服务启动后每过5秒,会由微服务内置的 Nacos 客户端主动向 Nacos 服务器发起心跳包(HeartBeat)。心跳包会包含当前服务实例的名称、IP、端口、集群名、权重等信息。
image.png
Nacos 注册中心的心跳机制
如果你开启微服务 Debug 日志,会清晰地看到每 5 秒一个心跳请求被发送到 Nacos 的 /nacos/v1/ns/instance/beat 接口,该请求会被 Nacos 服务器内置的 naming 模块处理。
23:11:23.826 DEBUG 10720 —- [ing.beat.sender] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@665891d213 pairs: {PUT /nacos/v1/ns/instance/beat?app=unknown&serviceName=DEFAULT_GROUP%40%40sample-service&namespaceId=public&port=9000&clusterName=DEFAULT&ip=192.168.47.1 HTTP/1.1: null}{Content-Type: application/x-www-form-urlencoded}{Accept-Charset: UTF-8}{Accept-Encoding: gzip,deflate,sdch}{Content-Encoding: gzip}{Client-Version: 1.3.2}{User-Agent: Nacos-Java-Client:v1.3.2}{RequestId: 6447aa06-9d70-41ea-83ef-cd27af1d3422}{Request-Module: Naming}{Host: 192.168.31.102:8848}{Accept: text/html, image/gif, image/jpeg, ; q=.2, /; q=.2}{Connection: keep-alive}{Content-Length: 326}
23:11:28.837 DEBUG 10720 —- [ing.beat.sender] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@5f00479a12 pairs: {PUT /nacos/v1/ns/instance/beat?app=unknown&serviceName=DEFAULT_GROUP%40%40sample-service&namespaceId=public&port=9000&clusterName=DEFAULT&ip=192.168.47.1 HTTP/1.1: null}{Content-Type: application/x-www-form-urlencoded}{Accept-Charset: UTF-8}{Accept-Encoding: gzip,deflate,sdch}{Content-Encoding: gzip}{Client-Version: 1.3.2}{User-Agent: Nacos-Java-Client:v1.3.2}{RequestId: 9fdf2264-9704-437f-bd34-7c9ee5e0be41}{Request-Module: Naming}{Host: 192.168.31.102:8848}{Accept: text/html, image/gif, image/jpeg,
; q=.2, /; q=.2}{Connection: keep-alive}
23:11:38.847 DEBUG 10720 —- [ing.beat.sender] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@3521283812 pairs: {PUT /nacos/v1/ns/instance/beat?app=unknown&serviceName=DEFAULT_GROUP%40%40sample-service&namespaceId=public&port=9000&clusterName=DEFAULT&ip=192.168.47.1 HTTP/1.1: null}{Content-Type: application/x-www-form-urlencoded}{Accept-Charset: UTF-8}{Accept-Encoding: gzip,deflate,sdch}{Content-Encoding: gzip}{Client-Version: 1.3.2}{User-Agent: Nacos-Java-Client:v1.3.2}{RequestId: ccb6a586-897f-4036-9c0d-c614e2ff370a}{Request-Module: Naming}{Host: 192.168.31.102:8848}{Accept: text/html, image/gif, image/jpeg, ; q=.2, /*; q=.2}{Connection: keep-alive}
naming 模块在接收到心跳包后,会按下图逻辑处理心跳包并返回响应:

  1. naming 模块收到心跳包,首先根据 IP 与端口判断 Nacos 是否存在该服务实例?如果实例信息不存在,在 Nacos 中注册登记该实例。而注册的本质是将新实例对象存储在“实例 Map”集合中;
  2. 如果实例信息已存在,记录本次心跳包发送时间;
  3. 设置实例状态为“健康”;
  4. 推送“微服务状态变更”消息;
  5. naming 模块返回心跳包时间间隔。

到这里一次完整的心跳包处理已完成。
image.png
Nacos Server 对心跳包的处理过程
那 Nacos 又是如何将无效实例从可用实例中剔除呢?Nacos Server 内置的逻辑是每过 20 秒对“实例 Map”中的所有“非健康”实例进行扫描,如发现“非健康”实例,随即从“实例 Map”中将该实例删除。

Nacos 与 Eureka 相比优势

  1. nacos 在自动或手动下线服务,使用消息机制通知客户端,服务实例的修改很快响应;Eureka 只能通过任务定时剔除无效的服务。
    nacos 可以根据 namespace 命名空间,DataId,Group 分组,来区分不同环境(dev,test,prod),不同项目的配置。

注册中心的原理

  • 服务实例在启动时注册到服务注册表,并在关闭时注销
  • 服务消费者查询服务注册表,获得可用实例
  • 服务注册中心需要调用服务实例的健康检查 API 来验证它是否能够处理请求

image.png

Nacos 服务地址动态感知原理

可以通过 subscribe 方法来实现监听,其中 serviceName 表示服务名、EventListener 表示监听到的事件:
image.png

具体调用方式如下:

image.png

或者调用 selectInstance 方法,如果将 subscribe 属性设置为 true,会自动注册监听:
image.png

image.png

Nacos 客户端中有一个 HostReactor 类,它的功能是实现服务的动态更新,基本原理是:

· 客户端发起时间订阅后,在 HostReactor 中有一个 UpdateTask 线程,每 10s 发送一次 Pull 请求,获得服务端最新的地址列表
· 对于服务端,它和服务提供者的实例之间维持了心跳检测,一旦服务提供者出现异常,则会发送一个 Push 消息给 Nacos 客户端,也就是服务端消费者
· 服务消费者收到请求之后,使用 HostReactor 中提供的 processServiceJSON 解析消息,并更新本地服务地址列表