Nacos是阿里巴巴的产品,也是目前SpringCloud的一个组件,相比Eureka而言,功能更加丰富,在国内受欢迎的程度也更高。
Nacos是需要下载的,Windows下载教程如下:
这是我在码云看到的一个热心网友分享的一个镜像,下载起来比较快,比起直接用家里的网络登录github快多了。这里,我们下载1.4.2的nacos:(镜像地址:Releases · alibaba/nacos · GitHub (xn–gzu630h.xn–kpry57d))
Release 1.4.2 (Apr 29th, 2021) · alibaba/nacos · GitHub (xn–gzu630h.xn–kpry57d)
进去后,我们拉到最下面,下载Windows版本的安装包,下载完成后解压到本地非中文的目录下。

我解压的位置是:D:\Program Files\nacos
nacos默认占用的端口号是:8848,如果该端口号已经被其他软件占领了,那么我们可以在 nacos/conf 目录下修改他的application.properties文件。
server.port=8848
nacos/target 目录下放置的是 nacos的jar包
要想启动nacos,我们可以通过cmd进入 nacos/bin 目录下,执行下列命令:(standalone 表示单机版)
startup.cmd -m standalone
启动成功后,我们访问网址 http://192.168.1.103:8848/nacos/index.html
初始账号密码都是nacos,登录成功后显示如下:

接下来,我们讲一下怎么简单的使用Nacos进行服务的注册。
要想在SpringCloud中使用Nacos,首先,我们对在原先项目中,引入我们的SpringCloud-Alibaba管理依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.5.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
然后,我们要注释掉之前在order还有user服务下的eureka依赖并添加nacos依赖。
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
之后,我们修改user和order的配置文件,注释掉Eureka的配置信息,并修改服务注册的地址为我们的nacos:
#eureka地址信息
#eureka:
# client:
# service-url:
# defaultZone: http://127.0.0.1:8090/eureka
spring:
cloud:
nacos:
server-addr: localhost:8848
一切准备就绪后,我们重启我们的三个服务(user、user1、order),然后,我们在nacos的控制台,可以看到:

点击服务右侧详情,我们还可以看到具体的每个服务的ip和端口号等详细信息,注意了,这里用的还是Windows,但是显示的是真ip地址,而不是像Eureka那样,Windows系统下,输出计算机名。

此时,我们访问地址:localhost:8080/order/101 仍然可以正常请求到数据。

服务多级存储模型:服务——集群——实例
举个例子:服务针对的是整个user这个微服务,而我们的user微服务在各个城市假设都有机房,并且每个机房我们建立集群,每个机房中的服务器,我们都整一个user服务实例,这就构成了我们的服务多级存储模型啦!具体可以看下面的图:

看到这个模型,有些小伙伴可能会说了,要这么玩的话,那以前面的例子,在北京的order访问我们的user,整一个负载均衡,是不是平均分到北京、上海、杭州这三个地方去了,那请求那么远,速度不就慢了吗。这就是我们的服务跨集群问题。针对这个问题,nacos的做法也很简单直接,既然服务调用跨集群延迟较高,那么服务调用就让他尽可能选择本地集群的服务,当本地集群不可访问的时候,我们再去访问其他集群。
给我们的服务实例配置集群也很简单,只需要在yaml中添加属性如下:
spring:
cloud:
nacos:
server-addr: localhost:8848 #nacos地址
discovery:
cluster-name: GZ #集群名称
我们可以在自己项目中两个User实例下将集群名称设置为SZ,再开一个实例将集群名称设置为GZ,结果如下:

我们设置order的集群为SZ。
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: SZ
再尝试发送3次请求,查看日志可以发现,三个user服务都被调用了,这明显不是我们想要的结果。我们希望的是集群为SZ的order 优先向 集群同为 SZ 的 user 服务发送请求,只有在user服务宕机的情况下,我们才考虑向其他的集群发送请求。
前面我们已经说过了,决定我们负载均衡的是Ribbon的IRule,其实Nacos也有提供一个IRule的实现类,要想实现Nacos集群内优先访问的负载均衡策略,我们可以使用Nacos包附带的NacosRule规则,在order服务的配置信息中添加对user服务请求时,IRule的配置信息如下:
user: #请求的服务名称
ribbon:
NFLoadBalanceRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #使用的负载均衡策略
如此,order在访问user服务的时候,他就会优先访问同一个集群的user服务,除非同一个集群的所有user服务宕机了,那么他才会访问其他集群的user服务。
批注:NacosRule 的请求方式是 相同集群内随机请求,如果相同集群所有服务全部宕机,那么随机请求其他集群的服务。
这种随机请求的方式对我们现实其实是不友好的,因为首先,企业的服务器一般不会说所有服务器都是同时租的or买的,而且每一台性能都一模一样,总有快慢之分。因此,我们肯定是希望性能好的服务器,他多被请求,性能差的服务器,少接收几个请求,而不是随机式的,怎么办呢?请看:
Nacos提供了权重配置来控制各个服务被请求的评率,具体只需要在nacos服务端,找到对应的服务,点击编辑,并修改其权重值即可(值为0~1),权重越小,被访问的概率就越小。


一般用来隔离我们的各个环境,例如最简单的 生产环境、测试环境和开发环境。
nacos默认会帮我们创建的命名空间叫public

我们也可以新建我们自己的命名空间,只需要点击左侧的命名空间

然后,点击右侧的新建命名空间,输入命名空间的名字和描述即可,命名空间的ID可以自己设置,也可以让他自动生成。

生成命名空间后,点击服务管理 》 服务列表,我们就可以看到我们的命名空间test,出现了右侧的上方了,点击即可跳转到对应的命名空间。

但是,新建的命名空间,它里面是没有任何服务的,我们要把对应的服务分配给我们的namespaces,我们就得记住我们对应的namespaces的id,比如我的id是: bf0be804-a669-449d-bb10-40aadd8b0a8e。
那么,我要将某个服务的实例配置到test这个命名空间下,我就在他的配置信息中添加如下配置:
spring:
cloud:
nacos:
discovery:
namespace: bf0be804-a669-449d-bb10-40aadd8b0a8e #命名空间的ID
我们可以把上面的配置信息配置到order服务下,然后重启order服务,再去看test这个命名空间下的服务情况:

此时,我们再访问order服务,由于user服务跟他不在同一个命名空间下,他将因为请求不到数据而报500错误。

Nacos和Eureka的共同点大概如下图:
1、服务的提供者向nacos/Eureka 注册中心进行服务信息的注册
2、服务的消费者并非每次需要服务都向注册中心拉取服务列表,而是定时(每隔30秒)发送请求获取我们的服务列表并进行缓存。
3、当消费者需要远程调用提供者的接口的时候,就会在我们的缓存中向分配到的提供者发送请求进行远程调用。

以上是Eureka和Nacos相同的地方,不同的点如下:
nacos对服务提供者有临时实例和非临时实例的区分,默认是临时实例。当提供者是临时实例的时候,他跟Eureka一样,服务需要定时向nacos/Eureka 注册中心发送请求(做心跳检测),告诉注册中心,我还能用。如果哪天临时实例宕机了,那么nacos会直接把他从服务列表中剔除。
而非临时实例就不一样了,非临时实例是nacos特有的,他是由nacos主动询问是否还活着,如果哪天非临时实例宕机了,nacos 也不会将他从服务列表中剔除,而是标识他处于不健康的状态,直到他重新开机。
nacos对服务消费者多了一个主动推送变更消息的功能,即,一旦nacos发现有服务提供者,他会立刻自动发消息给消费者,让他及时更新服务列表缓存,避免了Eureka定时拉取的30秒间隔内提供者宕机导致的异常。

要想设置实例为非临时实例,使用以下配置信息:
spring:
cloud:
nacos:
discovery:
ephemeral: false #非临时实例
Nacos除了可以做注册中心,同样可以做配置管理来使用。
当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。我们需要一种统一配置管理方案,可以集中管理所有实例的配置。
Nacos一方面可以将配置集中管理,另一方可以在配置变更时,及时通知微服务,实现配置的热更新。
配置步骤如下:
(1)打开我们的Nacos,点击左侧的配置管理 》 配置列表,然后点击右侧加号,添加配置文件

(2)书写我们的配置文件名和需要热更新的配置信息,我们的DataID也就是我们的文件名,一般取名的规则是服务名+环境 +后缀(.yaml)。

举个实例:
针对我们开发环境的user服务,我们给他取名叫 user-dev.yaml,配置格式为YAML,而生产环境需要输出的日志格式是年月日 时分秒。那么配置信息就如下所示:

注意:项目的核心配置,需要热更新的配置才有放到nacos管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好。
进行Nacos配置管理前,我们获取配置的步骤如下:
进行Nacos配置管理后,我们获取配置的步骤如下:
但是,问题来了,要想读取nacos中的配置文件信息,我们首先需要知道我们的nacos的地址,而nacos的地址被我们放在了本地配置文件中,这不就坏起来了吗?这怎么玩?这个时候,我们就需要用到我们的bootstrap.yaml文件了。项目启动后,我们会首先读取这个bootstrap.yaml文件,然后再去读取nacos中的配置文件,因此,我们只需要将nacos的配置信息放到bootstrap.yaml文件中就可以啦。
因此,为了拉取Nacos中的配置管理信息,我们需要以下几个步骤:
步骤一、在需要进行热更新的服务中导入nacos的配置管理依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
步骤二、在服务中新建bootstrap.yaml文件,这个文件是引导文件,优先级高级application.yaml。这里需要注意一点哈,spring.appliction.name的值 + spring.profiles.active的值 + .yaml 等于我们的配置管理的DataID。也就是上面配置的 user-dev.yaml。
spring:
application:
name: user #服务名称
profiles:
active: dev #开发环境(dev)
cloud:
nacos:
server-addr: localhost:8848 #Nacos地址
config:
file-extension: yaml #配置管理文件的文件后缀
鉴于之前的项目,我们把nacos的配置信息写在application.yaml中了,我们还需要把对应的信息删掉,user服务的application.yaml文件最终如下:
#端口号
server:
port: 8081
#数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/user?useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
# #服务名称
# application:
# name: user
# cloud:
# nacos:
# server-addr: localhost:8848 #nacos地址
# discovery:
# cluster-name: SZ #集群名称
# ephemeral: false #非临时实例
#mybatis
mybatis:
type-aliases-package: com.example.user.pojo
configuration:
map-underscore-to-camel-case: true
#日志
logging:
level:
cn.itcast: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
#eureka:
# client:
# service-url:
# defaultZone: http://127.0.0.1:8090/eureka
上面几个步骤,只是让我们的配置管理信息能够被服务器成功拉取下来,但是,还不能实现配置的热更新。要想实现配置的热更新,有以下两种方法:
方法一、 在@Value注入的变量所在的类上添加注解@RefreshScope
@RestController
@Slf4j
@RefreshScope
public class UserController {
@Value("{pattern.dateformat}")
private String dateformat;
...
}
如果想要查看/验证我们的信息是否成功拉取,可以写个Get请求如下:
@RestController
@Slf4j
@RefreshScope
public class UserController {
@Value("{pattern.dateformat}")
private String dateformat;
@GetMapping("/getDate")
public String getDate(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
}
}
方法二、使用@ConfigurationProperties注解
@Data
@Component
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
private String dateformat;
}
如果想要查看/验证我们的信息是否成功拉取,可以写个Get请求如下:
@RestController
@Slf4j
public class UserController {
@Resource
private PatternProperties properties;
@GetMapping("/getDate")
public String getDate(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(properties.getDateformat()));
}
对于部分适用于我们所有环境的配置信息,我们也可以做多环境共享配置。
微服务启动的时候,会从nacos中读取多个配置文件:
做个测试,我们再添加一个配置:

然后我们修改PatternProperties类如下:
package com.example.user.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
private String dateformat;
private String envShareValue;
}
之后我们写一个prop的控制器方法中把这两个值都输出来看一下:
@GetMapping("prop")
public PatternProperties properties(){
return properties;
}

注意,多种配置环境的优先级为:
服务名-环境名.yaml > 服务名.yaml > 本地配置(application.yaml)