SpringCloudAlibaba注册中心与配置中心之利器Nacos实战与源码分析(下)

源码资料

文档资料

<<Nacos架构与原理>>书籍于2021.12.21发布,并在Nacos官方网站非常Nice的提供其电子书的下载。我们学习Nacos源码更多是要吸取其优秀的设计理念和思想,这个和我们学习其他开源项目的初衷是一致的。

Nacos架构与原理 https://developer.aliyun.com/topic/download?id=8230

微服务治理技术白皮书 https://developer.aliyun.com/ebook/download/7565?spm=a2c6h.26392459.ebook-detail.3.12d959e7lHLVZ8

关于源码分析如果有时间还可以看早之前谭峰写的<<Spring Cloud Alibaba微服务原理与实战>>

Nacos读音为/nɑ:kəʊs/ ,是 Dynamic Naming and Configuration Service 的首字母简称;⼀个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos来源阿里内部三个项目( Configserver/Diamond/ Vipserver 内核)的合体。主要优势为易用、稳定、实时、规模。

阿里微服务 DNS(Dubbo+Nacos+Spring-cloud-alibaba/Seata/ Sentinel)最佳实践集性能和应用一体,是目前 Java 微服务生态最佳解决方案。

下载源码

Nacos GitHub源码 https://github.com/alibaba/nacos/releases 最新版本为2.1.0 (Apr 29, 2022),而1系列最新版本为1.4.3

源码启动

导入Idea后执行mvn compile,等待项目编译,编译完成信息如下

image-20220502103029954

打开Nacos的Main类,可以看到是一个SpringBoot程序,nacos-console控制台项目配置启动的参数如下

-Dnacos.standalone=true -Dnacos.home=C:\\nacos 

image-20220502103340054

启动Nacos,一个本地内嵌数据库模式就成功启动了

image-20220502103602762

访问http://localhost:8848/nacos ,输入用户和密码nacos/nacos进入控制台主页面

image-20220502103641856

总体设计

设计原则

  • 极简原则,简单才好用,简单才稳定,简单才易协作。
  • 架构⼀致性,⼀套架构要能适应开源、内部、商业化(公有云及专有云)3 个场景。
  • 扩展性,以开源为内核,商业化做基础,充分扩展,方便用户扩展。
  • 模块化,将通用部分抽象下沉,提升代码复用和健壮性。
  • 长期主义,不是要⼀个能支撑未来 3 年的架构,而是要能够支撑 10 年的架构。
  • 开放性,设计和讨论保持社区互动和透明,方便大家协作。

分层架构

整体架构分为用户层、业务层、内核层和插件,用户层主要解决用户使用的易用性问题,业务层主要解决服务发现和配置管理的功能问题,内核层解决分布式系统⼀致性、存储、高可用等核心问题, 插件解决扩展性问题。

配置模型

基础模型

  • Nacos 提供可视化的控制台,可以对配置进行发布、更新、删除、灰度、版本管理等功能。
  • SDK 可以提供发布配置、更新配置、监听配置等功能。
  • SDK 通过 GRPC 长连接监听配置变更,Server 端对比 Client 端配置的 MD5 和本地 MD5 是否相等,不相等推送配置变更。
  • SDK 会保存配置的快照,当服务端出现问题的时候从本地获取。

image-20220502113714411

配置资源模型

Namespace 的设计就是用来进行资源隔离的,我们在进行配置资源的时候可以从以下两个角度来 看:

  • 从单个租户的角度来看,我们要配置多套环境的配置,可以根据不同的环境来创建 Namespace。
  • 从多个租户的角度来看,每个租户都可以有自己的命名空间。

配置存储模型

  • config_info 存储配置信息的主表,里面包含 dataId、groupId、content、tenantId、encryptedDataKey 等数据。
  • config_info_beta 灰度测试的配置信息表,存储的内容和 config_info 基本相似。有⼀个 beta_ips 字段用于客户端请求配置时判断是否是灰度的 ip。
  • config_tags_relation 配置的标签表,在发布配置的时候如果指定了标签,那么会把标签和配置的关联信息存储在该表中。
  • his_config_info 配置的历史信息表,在配置的发布、更新、删除等操作都会记录⼀条数据,可以做多版本管理和快速回滚。

内核设计

Nacos一致性协议

Nacos在集群模式下,需要考虑如何保障各个节点之间 的数据⼀致性以及数据同步,而要解决这个问题,就不得不引入共识算法,通过算法来保障各个节点之间的数据的⼀致性。基于Nacos 是⼀个集服务注册发现以及配置管理于⼀体的组件场景触发,因此对于集群下各个节点之间的数据⼀致性保障问题,需要拆分成两个方面:

  • 从服务注册发现来看
    • 服务发现注册中心,在当前微服务体系下,是十分重要的组件,服务之间感知对方服务的当前可正常提供服务的实例信息,必须从服务发现注册中心进行获取,因此对于服务注册发现中心组件的可用性,提出了很高的要求,需要在任何场景下,尽最大可能保证服务注册发现能力可以对外提供服务;同时 Nacos 的服务注册发现设计,采取了心跳可自动完成服务数据补偿的机制。如果数据丢 失的话,是可以通过该机制快速弥补数据丢失。
    • 因此,为了满足服务发现注册中心的可用性,强⼀致性的共识算法这里就不太合适了,因为强⼀致性共识算法能否对外提供服务是有要求的,如果当前集群可用的节点数没有过半的话,整个算法直 接“罢工”,而最终⼀致共识算法的话,更多保障服务的可用性,并且能够保证在⼀定的时间内各 个节点之间的数据能够达成⼀致。
    • 上述的都是针对于 Nacos 服务发现注册中的非持久化服务而言(即需要客户端上报心跳进行服务实例续约)。而对于 Nacos 服务发现注册中的持久化服务,因为所有的数据都是直接使用调用 Nacos 服务端直接创建,因此需要由 Nacos 保障数据在各个节点之间的强⼀致性,故而针对此类型的服务数据,选择了强⼀致性共识算法来保障数据的⼀致性。
  • 从配置管理来看
    • 配置数据,是直接在 Nacos 服务端进行创建并进行管理的,必须保证大部分的节点都保存了此配置数据才能认为配置被成功保存了,否则就会丢失配置的变更,如果出现这种情况,问题是很严重的,如果是发布重要配置变更出现了丢失变更动作的情况,那多半就要引起严重的现网故障了,因此对于配置数据的管理,是必须要求集群中大部分的节点是强⼀致的,而这里的话只能使用强⼀致性共识算法。
  • Nacos一致性协议算法选择
    • 强⼀致性共识算法:当前工业生产中,最多使用的就是 Raft 协议,Raft 协议更容易让人理解, 并且有很多成熟的工业算法实现,比如蚂蚁金服的 JRaft、Zookeeper 的 ZAB、Consul 的 Raft、 百度的 braft、Apache Ratis,Nacos选择了支持多 RaftGroup的 JRaft,为 Nacos 后面的多数据分片带来了可能。
    • 最终⼀致性协议:Distro 协议是阿里巴巴自研的⼀个最终⼀致性协议,而最终⼀致性协议有很多,比如 Gossip、 Eureka 内的数据同步算法。而 Distro 算法是集 Gossip 以及 Eureka 协议的优点并加以优化而出来的。

image-20220502121020218

Nacos⼀致性协议最最基础的写动作和读动作两个方法如下

image-20220502121242799

任何使用⼀致性协议的,都只需要使用 getData 以及 write 方法即可。Nacos 对于 AP、CP 的⼀致性协议接口使用抽象都在 consistency 包,并且在实现具体的⼀致性协议时,采用了插件可插拔的形式,进⼀步将⼀致性协议具体实现逻辑和服务注册发现、配置管理两个模块达到解耦的目的。

此外nacos还通过数据存储抽象来统一有数据存储层管理保障数据存储以及多节点⼀致性。如KvStorage接口

image-20220502192822406

存储层进⼀步实现插件化的设计,也实现 Nacos 的计算层与存储层彻底分离。

Nacos自研的Distro协议

Distro 协议是 Nacos 社区自研的⼀种 AP 分布式协议,是面向临时实例设计的⼀种分布式协议, 其保证了在某些 Nacos 节点宕机后,整个临时实例处理系统依旧可以正常工作。作为⼀种有状态的中间件应用的内嵌协议,Distro 保证了各个 Nacos 节点对于海量注册请求的统⼀协调和存储。Distro的设计思想

  • Nacos 每个节点是平等的都可以处理写请求,同时把新数据同步到其他节点。
  • 每个节点只负责部分数据,定时发送自己负责数据的校验值到其他节点来保持数据⼀致性。
  • 每个节点独立处理读请求,及时从本地发出响应。

Distro 协议是 Nacos 对于临时实例数据开发的⼀致性协议。其数据存储在缓存中,并且会在启动时进行全量数据同步,并定期进行数据校验。

在 Distro 协议的设计思想下,每个 Distro 节点都可以接收到读写请求。所有的 Distro 协议的请求场景主要分为三种情况:

  • 当该节点接收到属于该节点负责的实例的写请求时,直接写入。
  • 当该节点接收到不属于该节点负责的实例的写请求时,将在集群内部路由,转发给对应的节点, 从而完成读写。
  • 当该节点接收到任何读请求时,都直接在本机查询并返回(因为所有实例都被同步到了每台机 器上)。

Distro 协议作为 Nacos 的内嵌临时实例⼀致性协议,保证了在分布式环境下每个节点上面的服务信息的状态都能够及时地通知其他节点,可以维持数十万量级服务实例的存储和⼀致性。

Nacos寻址机制

Nacos 支持单机部署以及集群部署,针对单机模式,Nacos 只是自己和自己通信;对于集群模式, 则集群内的每个 Nacos 成员都需要相互通信。无论是单机模式,还是集群模式,其根本区别只是 Nacos 成员节点的个数是单个还是多个,并且, 要能够感知到节点的变更情况:节点是增加了还是减少了;当前最新的成员列表信息是什么;以何 种方式去管理成员列表信息;如何快速的支持新的、更优秀的成员列表管理模式等等。 因此抽象出了⼀个 MemberLookup 接口
image-20220502192912074

  • 单机寻址:找到自己的 IP:PORT 组合信息,然后格式化为⼀个节点信息, 调用 afterLookup 然后将信息存储到 ServerMemberManager 中。

image-20220502194203109

  • 文件寻址:是 Nacos 集群模式下的默认寻址实现。文件寻址模式很简单,其实就是每个 Nacos节点需要维护⼀个叫做 cluster.conf 的文件。该文件默认只需要填写每个成员节点的 IP 信息即可,端口会自动选择 Nacos 的默认端口 8848, 如过说有特殊需求更改了 Nacos 的端口信息,则需要在该文件将该节点的完整网路地址信息补充 完整(IP:PORT)。 当 Nacos 节点启动时,会读取该文件的内容,然后将文件内的 IP 解析为节点列表,调用 afterLookup 存入 ServerMemberManager。如果发现集群扩缩容,那么就需要修改每个 Nacos 节点下的 cluster.conf 文件,然后 Nacos 内部的文件变动监听中心会自动发现文件修改,重新读取文件内容、加载 IP 列表信息、更新新增的节点。

image-20220502194727553

  • 地址服务器寻址:地址服务器寻址模式是 Nacos 官方推荐的⼀种集群成员节点信息管理,该模式利用了⼀个简易的web 服务器,用于管理 cluster.conf 文件的内容信息,这样,运维人员只需要管理这⼀份集群成员节点内容即可,而每个 Nacos 成员节点,只需要向这个 web 节点定时请求当前最新的集群成员节

    点列表信息即可。通过地址服务器这种模式,大大简化了 Nacos 集群节点管理的成本,同时,地址服务器是⼀个非常简单的 web 程序,其程序的稳定性能够得到很好的保障。

image-20220502195030920

image-20220502194954397

Nacos服务发现模块

设计原理

数据模型

Nacos 在经过内部多年生产经验后提炼出的数据模型,则是⼀种服务-集群-实例的三层模型;Nacos 提供了四层的数据逻辑隔离模型,用户账号对应的可能是⼀个企业或者独立的个体,⼀个用户账号可以新建多个命名空间。

临时实例和持久化实例

定义上区分临时实例和持久化实例的关键是健康检查的方式,临时实例使用客户端上报模式,而持久化实例使用服务端反向探测模式。⼀些基础的组件例如数据库、缓存等,这些往往 不能上报心跳,这种类型的服务在注册时,就需要作为持久化实例注册。而上层的业务服务,例如微服务或者 Dubbo 服务,服务的 Provider 端支持添加汇报心跳的逻辑,此时就可以使用动态服 务的注册方式。

数据⼀致性

⼀致性的选型目前来看基本可以归为两家:⼀种是基于 Leader 的非对等部署的单点写⼀致性,⼀种是对等部署的多写⼀致性。

  • 当注册的服务节点不会 定时发送心跳到注册中心时,强⼀致协议看起来是唯⼀的选择,因为无法通过心跳来进行数据的补偿注册,第⼀次注册就必须保证数据不会丢失。
  • 而当客户端会定时发送心跳来汇报健康状态时,第⼀次的注册的成功率并不是非常关键(当然也很关键,只是相对来说我们容忍数据的少量写失败), 因为后续还可以通过心跳再把数据补偿上来。

Nacos 因为要支持多种服务类型的注册,并能够具有机房容灾、集群扩展等必不可少的能力,在1.0.0 正式支持 AP 和 CP 两种⼀致性协议并存。⼀致性协议实现,⼀个是基于简化的 Raft 的 CP ⼀致性(基于 Leader 进行写入,其 CP 也并不是严格的,只是能保证⼀半所见⼀致,以及数据的丢失概率较小),⼀个是基于自研协议 Distro 的 AP ⼀致性。

健康检查

Nacos 目前支持临时实例使用心跳上报方式维持活性,发送心跳的周期默认是 5 秒,Nacos 服务端会在 15 秒没收到心跳后将实例设置为不健康,在 30 秒没收到心跳时将这个临时实例摘除。客户端健康检查主要关注客户端上报心跳的方式、服务端摘除不健康客户端的机制。而服务端健康检查,则关注探测客户端的方式、灵敏度及设置客户端健康状态的机制。Nacos 既支持客户端的健康检查,也支持服务端的健康检查,同⼀个服务可以切换健康检查模式。

性能与容量

影响读写性能的因素很多:⼀致性协议、机器的配置、集群的规模、存量数据的规模、数据结构及读写逻辑的设计等等。

服务数据模型

  • 定义服务

    • 命名空间(Namespace):Nacos 数据模型中最顶层、也是包含范围最广的概念,用于在类似环境或租户等需要强制隔离的场景中定义。Nacos 的服务也需要使用命名空间来进行隔离。
    • 分组(Group):Nacos 数据模型中次于命名空间的⼀种隔离概念,区别于命名空间的强制隔离属性,分组属于⼀个弱隔离概念,主要用于逻辑区分⼀些服务使用场景或不同应用的同名服务, 最常用的情况主要是同⼀个服务的测试分组和生产分组、或者将应用名作为分组以防止不同应用提供的服务重名。
    • 服务名(Name):该服务实际的名字,⼀般用于描述该服务提供了某种功能或能力。
  • 服务元数据

    • 进⼀步定义了 Nacos 中服务的细节属性和描述信息,包括健康保护阈值、实例选择器(Selector)、 拓展数据(extendData)。
  • 实例

    • 网络 IP 地址:该实例的 IP 地址,在 Nacos2.0 版本后支持设置为域名。
    • 网络端口:该实例的端口信息。
    • 健康状态(Healthy):用于表示该实例是否为健康状态。
    • 集群(Cluster):用于标示该实例归属于哪个逻辑集群。
    • 拓展数据(extendData):用于用户自定义扩展的元数据内容,形式为 K-V。可以在实例中拓展该实例的元数据信息,方便用户实现自己的自定义逻辑和标示该实例。
  • 实例元数据:权重、上线状态、拓展数据。

⽣命周期

  • 服务的⽣命周期
    • 直接创建服务
    • 注册实例时自动创建服务
  • 实例的生命周期
    • 实例的生命周期开始于注册实例的请求。
    • 持久化的实例会通过健康检查的状态维护健康状态,但是不会自动的终止该实例的生命周期。
    • 而非持久化的实例,会根据版本的不同,采用不同的方式维持健康状态。

健康检查

  • 第⼀种方式是客户端主动上报,告诉服务端自己健康状态,如果在⼀段时间没有上报,那么我们就认为服务已经不健康。对于健康检查机制主要都采用了 TTL(Time To Live)机制,即客户端在 ⼀定时间没有向注册中心发送心跳,那么注册中心会认为此服务不健康,进而触发后续的剔除逻辑。
  • 第二种,则是服务端主动向客户端进行探测,检查客户端是否还被能探测到。

image-20220502210229316

  • 临时实例健康检查机制:在 Nacos 中,用户可以通过两种方式进行临时实例的注册,通过 Nacos 的 OpenAPI 进行服务注册或通过 Nacos 提供的 SDK 进行服务注册。

    • OpenAPI 的注册方式实际是用户根据自身需求调用 Http 接口对服务进行注册,然后通过 Http 接口发送心跳到注册中心。在注册服务的同时会注册⼀个全局的客户端心跳检测的任务。在服务⼀段时间没有收到来自客户端的心跳后,该任务会将其标记为不健康,如果在间隔的时间内还未收到心跳,那么该任务会将其剔除。
    • SDK 的注册方式实际是通过 RPC 与注册中心保持连接(Nacos 2.x 版本中,旧版的还是仍然通过OpenAPI 的方式),客户端会定时的通过 RPC 连接向 Nacos 注册中心发送心跳,保持连接的存活。如果客户端和注册中心的连接断开,那么注册中心会主动剔除该 client 所注册的服务,达到下线的效果。同时 Nacos 注册中心还会在注册中心启动时,注册⼀个过期客户端清除的定时任务,用于删除那些健康状态超过⼀段时间的客户端。

    image-20220502211229390

  • 永久实例健康检查机制:永久实例的的健康检查,Nacos 采用的是注册中心探测机制,注册中心会在永久服务初始化时根据客户端选择的协议类型注册探活的定时任务。Nacos 现在内置提供了三种探测的协议,即Http、TCP 以及 MySQL 。

image-20220503014228303

Nacos配置管理模块

配置一致性模型

Nacos 配置管理⼀致性协议包括 Server 间⼀致性协议和 SDK 与Server 的⼀致性协议,配置作为分布式系统中非强⼀致数据,在出现脑裂的时候可用性高于⼀致性, 因此阿里配置中心是采用 AP ⼀致性协议。

  • Server 间的⼀致性协议
    • 无 DB 模式:Server 间采用 Raft 协议保证数据⼀致性。
    • 有 DB 模式(读写分离架构):⼀致性的核心是 Server 与 DB 保持数据⼀致性,从而保证 Server 数据⼀致;Server 之间都是对等的。数据写任何⼀个 Server,优先持久化,持久化成功后异步通知其他节点到数据库中拉取最新配置值,并且通知写入成功。

image-20220503015324452

  • SDK 与 Server 的⼀致性协议
    • SDK 与 Server ⼀致性协议的核心是通过 MD5 值是否⼀致,如果不⼀致就拉取最新值。
      • Nacos 1.X 采用 Http 1.1 短链接模拟长链接,每 30s 发⼀个心跳跟 Server 对比 SDK 配置 MD5 值是否跟 Server 保持⼀致,如果⼀致就 hold 住链接,如果有不⼀致配置,就把不⼀致的配置返回,然后 SDK 获取最新配置值。
      • Nacos 2.x 相比上面 30s ⼀次的长轮训,升级成长链接模式,配置变更,启动建立长链接,配置变更服务端推送变更配置列表,然后 SDK 拉取配置更新,因此通信效率大幅提升。

客户端的长轮询是通过反射来完成NacosConfigService的实例化

image-20220503021124458

客户端长轮询也是一个线程,启动定时任务

image-20220503021708413

Nacos高可用

  • 同城容灾
    • Nacos 本身是采用 AP 的⼀致性模式,同 Region 多个可用区部署,任何⼀个可用区出问题,剩下部分继续工作。
  • 数据多级容灾
    • Nacos 持久化存储做了主备容灾,而且底层存储数据多副本高可用保障。
    • Nacos Server 有全量缓存数据,即使存储挂或者不可用,只影响写,核心的读服务不受影响。
    • Nacos SDK 有所需服务和配置缓存,Server 即使全挂,走本地缓存,保证核心业务调用不受影响。

**本人博客网站 **IT小神 www.itxiaoshen.com