Nacos(Naming Configuration Service) 是一个易于使用的动态服务发现、配置和服务管理平台,用于构建云原生应用程序。而服务发现是微服务架构中的关键组件之一,Nacos 致力于发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。帮助更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
简单的可以理解为:Nacos = 注册中心 + 配置中心组合,如果有接触过Netflix的Eureka的话,会发现这二者的作用几乎是一样的,Nacos是在Eureka的注册中心的基础之上增加了配置中心,Nacos支持几乎所有主流类型的“服务”的发现、配置和管理,常见的服务有:Kubernetes Service、gRPC & Dubbo RPC Service、Spring Cloud RESTful Service等。
为何使用注册中心呢?我们以入住酒店的前台为例子,稍微加以解释。先设想一个没有服务前台的酒店,客人入住需要自己寻找合适居住的房间,客人不知道每个房间的情况,无法确定那个房间是打扫干净可以入住,客人只能逐个房间寻找,如果遇到已经居住房客的房间一定很尴尬,显然这是不正常的情况。正常的情况是酒店会安排服务台,酒店打扫干净可以入住的房间会登记注册到服务台,这样客人来住店,只需要在前台就可以查找到可以入住的房间,这样就无需等待快速的入住。显然,服务器提供发注册和发现机制可以让房客快速找到合适的房间,快速解决入住问题。
采用微服务以后,软件微服务组件各自独立,但最终还要组合为一个整体作为一个软件系统服务于最终客户,这时软件组件之间也需要彼此通讯,彼此调用方法。微服务架构内部发起通讯调用方法的一方成为“服务消费者”,提供远程方法调用的服务器称为“服务提供者”,往往为了提高系统性能,会提供多个服务器作为服务提供者,此时服务消费者找到服务提供者的过程,就类似于用户在找房间的过程。为了帮助服务消费者快速的发现服务提供者,在微服务框架中都会引入注册中心。注册中心类似于酒店的前台,提供在软件服务的注册和发现功能,服务提供者会先在注册中心进行注册,声明可以对外提供服务,而服务消费者只需要在注册中心就可以快速发现找到可以使用的服务,快速使用服务,注册中心实现了服务提供和服务消费的快速撮合功能。
Nacos官网网址:https://nacos.io/zh-cn/index.html
官网提供了最最权威和最最详细的使用手册:https://nacos.io/zh-cn/docs/what-is-nacos.html
官网给出的建议推荐的稳定版本为 2.0.3,并且Nacos依赖于JDK环境和Maven环境,安装Nacos之前要确保已经安装JDK1.8及以上版本和Maven3.2及以上版本。
将下载好的Nacos安装包进行解压,进入到解压后的Nacos的bin目录下,打开cmd窗口:
将Nacos以单机服务启动:startup.cmd -m standalone
访问Nacos服务,验证是否启动成功:
http://localhost:8848/nacos/#/login
默认登录账号和密码都是nacos,登录进去:
当你成功看到以上的这个界面,就证明你的Nacos服务已经启动成功!
服务发现是微服务架构中的关键组件之一。在这样的架构中,手动为每个客户端配置服务列表可能是一项艰巨的任务,并且使得动态扩展极其困难。Nacos Discovery 帮助您自动将您的服务注册到 Nacos 服务器,Nacos 服务器会跟踪服务并动态刷新服务列表。此外,Nacos Discovery 将服务实例的一些元数据,如主机、端口、健康检查 URL、主页等注册到 Nacos,也就是说可以Nacos提供的服务列表中看到相关的服务及服务的详细信息。
SpringCloud Alibaba官网提供了最最权威和最最详细的使用说明:
https://spring.io/projects/spring-cloud-alibaba#learn
还是希望大家平时有空的时候多去看看官方网站。
由于聚合带来的诸多好处,在SpringBoot项目开发中也广泛采用,开发中将SpringBoot项目按照功能分成子模块开发,所以我们在使用Spring Cloud Alibaba完成项目的时候,也是采用聚合项目来完成。
先创建一个父项目:
在父项目中引入spring-cloud-alibaba-dependencies的依赖坐标进行管理
在父项目中创建子模块:
在子项目中使用parent标签继承父项目,并引入spring-cloud-starter-alibaba-nacos-discovery的jar包依赖
然后在父项目的pom文件中将子模块进行module聚合
在nacos-9001项目中进行nacos服务相关的配置
在nacos-9001项目中添加@EnableDiscoveryClient注解
添加业务类方便对外接受访问
启动nacos-9001项目,在nacos服务列表中可以看到已经成功启动的nacos-9001服务及详情
注:不少小伙伴按照上述步骤搭建下来,发现nacos-9001服务一启动就报错,一启动就报错,反复排查配置文件也没有发现错误,其实可能是因为SpringBoot和SpringCloudAlibaba的对应版本错误产生项目启动报错。我现在使用的SpringBoot的版本为2.7.3,对应的SpringCloudAlibaba的版本是2021.0.4.0,官网会上详细说明版本之间的对应关系:
https://spring-cloud-alibaba-group.github.io/github-pages/2021/en-us/index.html
SpringBoot和SpringCloudAlibaba之间的版本对应正确时候,启动项目之前先确保nacos服务启动成功,再启动nacos-9001项目,顺序不可颠倒:
按照上面的步骤,再创建一个nacos-9002的项目:
在父项目中再新建一个消费者子模块
启动consumer项目,在nacos服务列表中查看服务消费者
走到这一步,也就意味着服务消费者已经被nacos成功的捕获并注册,但是现在仅仅是创建,使用要怎么使用?接下来就该Ribbon上场进行服务之间调用协调。
它是一个基于HTTP和TCP客户端负载均衡器。它虽然只是一个工具类库,它却是每一个微服务的基础设施。因为实际上,对于服务间调用、API网关请求转发都需要经过Ribbon负载均衡来实现。总体来说,Ribbon的主要作用是:从注册服务器端拿到对应服务列表后以负载均衡的方式访问对应服务。要注意的是Nacos已经整合了Ribbon,所以我们想要使用只需要导入Spring Cloud Alibaba Nacos的依赖就可以直接使用了。
在消费者服务的yml文件中配置需要访问的服务名称
在消费者的启动类上配置@EnableDiscoveryClient注解和RestTemplate类
在消费者项目中的Controller中,通过方法restTemplate.getForObject调用提供者的方法:
getForObject方法中:
第一个参数:Nacos中注册的服务提供者名称+调用方法的请求路径;
第二个参数:方法调用返回值类型,如果控制器返回的是List集合,需要使用数组类型接;
理论上按照这样就可以在9001和9002之间通过nacos的ribbon负载均衡自动进行方法调用,可是访问8083这个方法得到的却是一个大大的报错:
不知道请求哪个提供者的方法,看起来似乎是负载均衡出了问题,两个提供者不知道找谁了,nacos中的ribbon不是自动负载均衡的吗?坑就在于上一篇中说的巨头之间打架,2018年12月Spring Cloud Netflix官网对外宣布进入维护模式
如下组件都进行了替换:
也就是说以前低版本当中引入注册中心,Cloud的Ribbon都会引入loadbalancer包,但是这次却没有发现loadbalancer的踪影,好像是cloud比较高的版本就没自动引入loadbalancer包,需要自己手动引入loadbalancer的依赖:
再次启动8083项目,即可通过getForObject方法以负载均衡的形式调用9001和9002的方法
首先我们需要知道CAP模型是什么东西,基本上所有的服务注册中心都是基于CAP模型所实现:
计算机专家 埃里克·布鲁尔(Eric Brewer)于 2000 年在 ACM分布式计算机原理专题讨论会(简称:PODC)中提出的分布式系统设计要考虑的三个核心要素:
一致性(Consistency):同一时刻的同一请求的实例返回的结果相同,所有的数据要求具有强一致性(Strong Consistency);
可用性(Availability):所有实例的读写请求在一定时间内可以得到正确的响应;
分区容错性(Partition tolerance):在网络异常(光缆断裂、设备故障、宕机)的情况下,系统仍能提供正常的服务;
以上三个特点就是CAP原则(又称CAP定理),但是三个特性不可能同时满足,所以分布式系统设计要考虑的是在满足P(分区容错性)的前提下选择C(一致性)还是A(可用性),即:CP或AP。
CP原则属于强一致性原则,要求所有节点可以查询的数据随时都要保持一直(同步中的数据不可查询),即:若干个节点形成一个逻辑的共享区域,某一个节点更新的数据都会立即同步到其他数据节点之中,当数据同步完成后才能返回成功的结果,但是在实际的运行过程中网络故障在所难免,如果此时若干个服务节点之间无法通讯时就会出现错误,从而牺牲了以可用性原则(A原则),例如关系型数据库中的事务。
AP原则属于弱一致性原则,在集群中只要有存活的节点那么所发送来的所有请求都可以得到正确的响应,在进行数据同步处理操作中即便某些节点没有成功的实现数据同步也返回成功,这样就牺牲一致性原则(C 原则)。一般来讲使用场景是对于数据的同步一定会发出指令,但是最终的节点是否真的实现了同步,并不保证,可是却可以及时的得到数据更新成功的响应,可以应用在网络环境不是很好的场景中。
服务注册与发现框架 | CAP模型 | 控制台管理 | 社区活跃度 |
---|---|---|---|
Eureka | AP | 支持 | 低(2.x版本闭源) |
Zookeeper | CP | 不支持 | 中 |
Consul | CP | 支持 | 高 |
Nacos | AP/CP | 支持 | 高 |
从上面表格中的对比就可以看到,Nacos在CAP模型方面是独树一帜的存在,其余的只能支持一个,但是Nacos既可以支持AP也可以支持CP。Eureka现在基本上已经闭源了,而且使用率非常低;Zookeeper倒是还有人在用,不过这货是Go语言写的。
Nacos无缝支持一些主流的开源生态,同时再阿里进行Nacos设计的时候重复的考虑到了市场化的运作(市面上大多都是以单一的实现形式为主,例如:Zookeeper使用的是 CP、而 Eureka采用的是AP),在Nacos中提供了两种模式的动态切换。
从这张官网上给出的图可以看出,阿里的野心不小,对于生态已经不仅仅是想插手而已。
一般来说,如果不需要储存服务界别的信息且服务实例通过nacos-client注册(就是引入spring-cloud-starter-alibaba-nacos-discovery依赖包),并能够保持心跳上报(就是注册在Nacos中的这些服务,会定时向Nacos发送消息,告诉Nacos哥们儿我还活着,别把我踢出群聊;Nacos自己也会定时去检测注册进来的这些服务,是否有按规矩发送心跳消息。超过设定的时间没有接收到心跳消息,就会把服务提出群聊),那么就可以选择AP模式。如Spring Cloud 和 Dubbo,都适用于AP模式,AP模式为了服务的可用性减弱了一致性,因此AP模式下只支持注册临时实例。
如果需要在服务级别编辑或者储存配置信息,那么CP是必须的,K8S服务和DNS服务则是用于CP模式。CP模式下则支持注册持久化实例,此时则是以Raft协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。
临时实例和持久化实例的区别在于:健康检查失败后的表现,持久化实例健康检查失败后会被标记成不健康,而临时实例会直接从列表中被删除。
Nacos默认使用的是AP模型,若要切换到CP模型,执行命令:
curl -X PUT '${NACOS_SERVER}:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
在父项目中新建一个子模块 cloudalibaba-config-3377,并引入 spring-cloud-starter-alibaba-nacos-discovery 和 spring-cloud-starter-alibaba-nacos-config 依赖包
配置 bootstrap.yml 配置文件
配置 application.yml 配置文件
要注意的是这里我们要配置两个,因为Nacos同SpringCloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动。SpringBoot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application,bootstrap.yml配置好了以后,作用是两个:第一个让3377这个服务注册到Nacos中;第二个作用就是去Nacos中去读取指定后缀为yaml的配置文。
老规矩,在主启动了中加入@EnableDiscoveryClient注解
写一个验证是否成功的业务类
在Nacos平台新增配置
新建配置中的Data ID好好捋一下:
根据Nacos官网给出的说明,Data ID要按照 ${prefix}-${spring.profiles.active}.${file-extension} 规则来填写:
`prefix` 默认为 `spring.application.name` 的值,也可以通过配置项 `spring.cloud.nacos.config.prefix`来配置。那么根据3377项目配置文件中的值,${prefix}就是`nacos-config-client`;
`spring.profiles.active`的值,根据3377项目配置文件中的值就是`dev`;
`file-extension`的值,根据3377项目配置文件中的值就是`yaml`;
所以综合拼起来的Data ID就是`nacos-config-client-dev.yaml`。
配置完成后发布,即可在nacos的配置列表中看到刚刚发布的配置
那么此时按照剧本启动项目,访问 http://localhost:3377/config/configInfo 就可以拿到nacos中刚刚配置的信息。但是此时启动项目就会导致报错!
产生问题的原因是bootstrap.properties比application.properties的优先级要高,由于bootstrap.properties是系统级的资源配置文件,是用在程序引导执行时更加早期配置信息读取。而application.properties是用户级的资源配置文件,是用来后续的一些配置所需要的公共参数。但是在SpringCloud 2020.* 版本把bootstrap禁用了,导致在读取文件的时候读取不到而报错,所以我们只要把bootstrap从新导入进来就会生效了。
此时再次启动项目后,就会在控制台看到
访问 http://localhost:3377/config/configInfo 即可拿到nacos中的配置内容
直接编辑nacos中配置文件内容,版本号1改为2
3377项目无需重启,直接刷新即可获取最新的配置文件内容
用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
Nacos 中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:不同的应用或组件使用了相同的配置类型,如 database_url 配置和 MQ_topic 配置。
Nacos 中的某个配置集的 ID。配置集 ID 是组织划分配置的维度之一。Data ID 通常用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。Data ID 通常采用类 Java 包(如 com.taobao.tc.refund.log.level)的命名规则保证全局唯一性。此命名规则非强制。
配置集:一组相关或者不相关的配置项的集合称为配置集。在系统中,一个配置文件通常就是一个配置集,包含了系统各个方面的配置。例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。
简单来说,在一个Namespace下可以存在多个Group,在一个Group下可以存在多个Data ID。这三者的关系类似于Java里面的package名和类名,最外层的Namespace是可以用于区分部署环境的,Group和DataID逻辑上区分两个目标对象。
这三者的默认情况:
Namespace=public,Group=DEFAULT_GROUP,默认Cluster是DEFAULT。
Nacos默认的命名空间是public,可以利用Namespace来实现隔离,比如现在有三个环境:开发、测试、生产环境,就可以创建三个Namespace,不同的Namespace之间是隔离的;
Group本身就是分组的意思,它可以把不同的微服务划分到同一个分组里面去;
剩下的就是具体微服务,一个Service可以包含多个Cluster,Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。比如说,将一个Service部署在北京和和杭州的机房中,北京机房的Service就可以起名为(BJ),杭州机房中的Service就可以起名为(HZ),这样就可以尽量让同一个机房的微服务互相调用,提升性能。
Data ID方案
指定spring.profile.active和配置文件的DataID来使不同环境下读取不同的配置,配置规则:默认空间+新建dev和test两个DataId。
dev的配置文件刚刚在前面已经创建好了(nacos-config-client-dev.yaml),现在再来新建一个test的配置文件
然后将application.yml文件中的${spring.profile.active}的值改为test
那么现在按照上面讲述的Data ID要按照 ${prefix}-${spring.profiles.active}.${file-extension} 规则就是:nacos-config-client-test.yaml
重启3377项目,访问http://localhost:3377/config/configInfo,可以看到读取的就是新的nacos-config-client-test.yaml文件中的内容
Group方案
根据之前的讲解,Group为分组默认是:DEFAULT_GROUP,所以现在我们就需要分出两组,一组是“dev开发组”,一组是“test测试组”。
可以看到,在不同的Group中刻意没有改Data ID,在不同的Group中是可以存在同名的Data ID,就好比在不同目录下同名的文件道理一样。完成以上配置以后,接下来就需要通过bootstrap+application来配合完成,具体方式:在config下增加一条Group的配置即可,可以配置为DEV_GROUP或TEST_GROUP
重启3377项目,访问http://localhost:3377/config/configInfo,可以看到读取的就是dev开发组中的的nacos-config-client-info.yaml文件中的内容
同样,也可以访问test测试组中的配置内容
Namespace方案
Nacos中默认的Namespace是不可以删除的,接下来还是新建两个Namespace
在test命名空间中,dev开发组下,新建一个dev开发配置文件
根据上配置规则,修改bootstrap和application配置文件
重启3377项目,访问http://localhost:3377/config/configInfo,可以看到读取的就是test命名空间下,dev开发组中的nacos-config-client-dev.yaml文件中的内容
Nacos默认自带嵌入式数据库Derby(从Nacos的Git源码上确实有看到引入了org.apache.derby的依赖包),所以我们每次创建一个Nacos实例就会有一个Derby数据库,创建了3个Nacos实例,就会有3个Derby数据库,数据库之间就会出现数据的一致性问题。Nacos为了解决这个数据一致性问题,支持了外部数据库统一数据管理MySQ,目前也只支持MySQL。
进入Nacos安装目录下的conf目录中,找到一个叫nacos-mysql.sql的数据库脚本文件
在本地MySQL服务中建立一个nacos_config数据库,然后执行脚本中的语句,生成所需的表
需要注意的是,这里面包含当前的用户名和密码,这个config_info表就是对应配置文件创建完以后,对应存储的表。
修改config目录下的application.properties文件,增加支持MySQL数据源配置,添加MySQL数据源的URL、用户名和密码:
此时再重启Nacos服务,会发现之前所创建的Namespace+Group+DataID将全部消失,因为这些数据是保存在之前Nacos内嵌的derby数据库中,现在使用的是本地的MySQL数据库,可以添加配置测试,在查看数据库nacos_config中的config_info表,此时就会有新添加的配置内容:
之前配置列表中的数据全部被清除了,再重新新增一条配置文件:
同步的在MySQL中的config_info表中也同步生成一条配置记录:
接下来基于上面的架构示意图,在CentOS系统下搭建一下Nacos的集群。不过演示阶段,Nginx和MySQL就不搭集群了,使用单机服务即可。
其实Nacos本身默认启动就是集群模式,以3个Nacos节点作为一个集群来说,在启动多个Nacos节点的时候,在启动的时候不需要-m standalone参数。Nacos在启动的时候后,对内存占用是比较大的,默认好像是启动一个要2G的内存,当然实际上是用不到这么大内存。
将参与集群的3台Nacos服务的host和port配置进去。值得注意的是:1、Nacos2.0以后增加了9848、9849端口进行GRPC通信,所以在修改Nacos服务端口的时候,注意避开这两个端口占用;2、Nacos在config目录下只提供了cluster.conf.example,需要手动复制一份cluster.conf文件出来进行配置。
如果在Nacos启动的过程中遇到【No DataSource set】错误的话,有可能是root用户没有开启远程登录权限,需要修改root权限为可以远程登录:
use mysql;(一定要使用mysql库,只能在这个库里授权)
update user set host='%' where user='root';
flush privileges;
数据库的配置设置好以后,再将3台Nacos以集群的模式启动即可
当看到日志输出Nacos的集群服务已经启动成功之后,可随便访问一台Nacos服务节点,登录Nacos服务后,来到集群管理的节点列表中,即可看到此集群中所有的Nacos节点信息
修改Nginx配置文件
看到这个配置方式,可能有些小伙伴会有疑问,这里为什么不用http协议,而使用tcp协议进行配置。主要还是因为Nacos自2.0之后新增的这个GRPC通信是基于长连接的方式,那在配置文件中就需要支持这种长连接的方式,并且GRPC通信本身其内部就是基于tcp协议,所以这里的配置就直接使用tcp协议的方式。而在之前的老版本中,比如常用的1.4.1版本中就还是使用http协议进行的配置,但是在2.0版本之后就要对配置的方式进行改变了。
但是这个stream模块可能会导致Nginx启动的时候会报错
这个也好解决,直接对源文件重新进行编译安装即可:
cd /usr/local/source/nginx-1.20.2
./configure --with-stream
make & make install
Nginx正常启动后直接访问
新建配置文件也可以正常同步至MySQL数据库中