
在微服务远程调用的过程中,包括两个角色:
服务提供者:提供接口供其它微服务访问,比如item-service
服务消费者:调用其它微服务提供的接口,比如cart-service
在大型微服务项目中,服务提供者的数量会非常多,为了管理这些服务就引入了注册中心的概念。注册中心、服务提供者、服务消费者三者间关系如下:

流程如下:
服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心
调用者可以从注册中心订阅想要的服务,获取服务对应的实例列表(1个服务可能多实例部署)
调用者自己对实例列表负载均衡,挑选一个实例
调用者向该实例发起远程调用
当服务提供者的实例宕机或者启动新实例时,调用者如何得知呢?
服务提供者会定期向注册中心发送请求,报告自己的健康状态(心跳请求)
当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除
当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表
当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

首推官方
docker run --name nacos-quick -e MODE=standalone -e JVM_XMS=256m -e JVM_XMX=256m -p 8848:8848 -p 9848:9848 -p 9849:9849 -p7848:7848 -d nacos/nacos-server:v2.2.3
cloudalibaba-provider-payment9001
pom.xml
- "1.0" encoding="UTF-8"?>
"http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
4.0.0 -
-
com.atguigu.cloud -
mscloudV5 -
1.0-SNAPSHOT -
-
-
cloudalibaba-provider-payment9001 -
-
-
17 -
17 -
UTF-8 -
-
-
-
-
-
-
-
com.alibaba.cloud -
spring-cloud-starter-alibaba-nacos-discovery -
-
-
-
com.atguigu.cloud -
cloud-api-commons -
1.0-SNAPSHOT -
-
-
-
org.springframework.boot -
spring-boot-starter-web -
-
-
org.springframework.boot -
spring-boot-starter-actuator -
-
-
-
cn.hutool -
hutool-all -
-
-
-
org.projectlombok -
lombok -
1.18.28 -
provided -
-
-
-
org.springframework.boot -
spring-boot-starter-test -
test -
-
-
-
-
-
-
org.springframework.boot -
spring-boot-maven-plugin -
-
-
-
yml配置
- server:
- port: 9001
-
- spring:
- application:
- name: nacos-payment-provider
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848 #配置Nacos地址
主程序
- @SpringBootApplication
- @EnableDiscoveryClient
- public class Main9001
- {
- public static void main(String[] args)
- {
- SpringApplication.run(Main9001.class,args);
- }
- }
业务类
- @RestController
- public class PayAlibabaController
- {
- @Value("${server.port}")
- private String serverPort;
-
- @GetMapping(value = "/pay/nacos/{id}")
- public String getPayInfo(@PathVariable("id") Integer id)
- {
- return "nacos registry, serverPort: "+ serverPort+"\t id"+id;
- }
- }
配置项
-
- server:
- port: 83
-
- spring:
- application:
- name: nacos-order-consumer
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848
- #消费者将要去访问的微服务名称(nacos微服务提供者叫什么你写什么)
- service-url:
- nacos-user-service: http://nacos-payment-provider
配置类
- @Configuration
- public class RestTemplateConfig
- {
- @Bean
- @LoadBalanced //赋予RestTemplate负载均衡的能力
- public RestTemplate restTemplate()
- {
- return new RestTemplate();
- }
- }
控制类
- @RestController
- public class OrderNacosController
- {
- @Resource
- private RestTemplate restTemplate;
-
- @Value("${service-url.nacos-user-service}")
- private String serverURL;
-
- @GetMapping("/consumer/pay/nacos/{id}")
- public String paymentInfo(@PathVariable("id") Integer id)
- {
- String result = restTemplate.getForObject(serverURL + "/pay/nacos/" + id, String.class);
- return result+"\t"+" 我是OrderNacosController83调用者。。。。。。";
- }
- }
pom
- "1.0" encoding="UTF-8"?>
"http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
-
mscloudV3 -
com.atguigu.cloud -
1.0-SNAPSHOT -
-
4.0.0 -
-
cloudalibaba-config-nacos-client3377 -
-
-
17 -
17 -
-
-
-
-
-
-
org.springframework.cloud -
spring-cloud-starter-bootstrap -
-
-
-
com.alibaba.cloud -
spring-cloud-starter-alibaba-nacos-config -
-
-
-
com.alibaba.cloud -
spring-cloud-starter-alibaba-nacos-discovery -
-
-
-
org.springframework.boot -
spring-boot-starter-web -
-
-
org.springframework.boot -
spring-boot-starter-actuator -
-
-
-
org.projectlombok -
lombok -
true -
-
-
-
-
-
-
org.springframework.boot -
spring-boot-maven-plugin -
-
-
Nacos同Consul一样,在项目初始化时,要保证先从配置中心进行配置拉取,
拉取配置之后,才能保证项目的正常启动,为了满足动态刷新和全局广播通知
springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application
- # nacos配置
- spring:
- application:
- name: nacos-config-client
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848 #Nacos服务注册中心地址
- config:
- server-addr: localhost:8848 #Nacos作为配置中心地址
- file-extension: yaml #指定yaml格式的配置
-
- # nacos端配置文件DataId的命名规则是:
- # ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
- # 本案例的DataID是:nacos-config-client-dev.yaml
- server:
- port: 3377
-
- spring:
- profiles:
- active: dev # 表示开发环境
- #active: prod # 表示生产环境
- #active: test # 表示测试环境
- @RestController
- @RefreshScope //在控制器类加入@RefreshScope注解使当前类下的配置支持Nacos的动态刷新功能。
- public class NacosConfigClientController
- {
- @Value("${config.info}")
- private String configInfo;
-
- @GetMapping("/config/info")
- public String getConfigInfo() {
- return configInfo;
- }
- }
@RefreshScope
@RefreshScope是一个用于 Spring Cloud Config 的注解,它允许在不重启应用程序的情况下重新加载配置信息。当配置中心的配置信息发生变化时,使用@RefreshScope注解的类会自动刷新配置信息,使得应用程序能够实时获取到最新的配置信息。使用
@RefreshScope注解的方法如下:
- 在需要刷新配置信息的类上添加
@RefreshScope注解。- 使用
@Value或@ConfigurationProperties注解来注入配置信息。- 调用
/actuator/refresh端点(需要在项目中引入 Spring Boot Actuator)来触发配置刷新。
Nacos中的dataid的组成格式及与SpringBooti配置文件中的匹配规则


Nacos会记录配置文件的历史版本默认保留30天,此外还有一键回滚功能,回滚操作将会触发配置更新

背景
问题1:
实际开发中,通常一个系统会准备
dev开发环境
test测试环境
prod生产环境。
如何保证指定环境启动时服务能正确读取到Nacos上相应环境的配置文件呢?
问题2:
一个大型分布式微服务系统会有很多微服务子项目,
每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境......
那怎么对这些微服务配置进行分组和命名空间管理呢?

|
1 是什么
|
类似Java里面的package名和类名,最外层的Namespace是可以用于区分部署环境的,
Group和DataID逻辑上区分两个目标对象
|
|
2 默认值
| 默认情况:Namespace=public,Group=DEFAULT_GROUP
Nacos默认的命名空间是public,Namespace主要用来实现隔离。比方说我们现在有三个环境
:开发、测试、生产环境,我们就可以创建三个Namespace,不同的Namespace之间
是隔离的。Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分
到同一个分组里面去
|
|
Service就是微服务
|
一个Service可以包含一个或者多个Cluster(集群),Nacos默认Cluster是DEFAULT,
Cluster是对指定微服务的一个虚拟划分。见下一节:服务领域模型-补充说明
|
指定spring.profile..active和配置文件的DatalD来使不同环境下读取不同的配置
- # nacos配置 第一种:默认空间+默认分组+新建DataID
- spring:
- application:
- name: nacos-config-client
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848 #Nacos服务注册中心地址
- config:
- server-addr: localhost:8848 #Nacos作为配置中心地址
- file-extension: yaml #指定yaml格式的配置
-
- # nacos端配置文件DataId的命名规则是:
- # ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
- # 本案例的DataID是:nacos-config-client-dev.yaml
-
- server:
- port: 3377
-
- spring:
- profiles:
- #active: dev # 表示开发环境
- active: test # 表示测试环境
- #active: prod # 表示生产环境
默认空间public+新建PROD GROUP+新建DatalD

1.引入依赖
在父工程的pom文件中的中引入SpringCloudAlibaba的依赖:
-
com.alibaba.cloud -
spring-cloud-alibaba-dependencies -
2.2.6.RELEASE - <type>pom
-
import
然后在user-service和order-service中的pom文件中引入nacos-discovery依赖:
-
com.alibaba.cloud -
spring-cloud-starter-alibaba-nacos-discovery
2.配置nacos地址
在user-service和order-service的application.yml中添加nacos地址:
- spring:
- cloud:
- nacos:
- server-addr: localhost:8848
一个服务可以有多个实例,例如我们的user-service,可以有:
假如这些实例分布于全国各地的不同机房,例如:
Nacos就将同一机房内的实例 划分为一个集群。
也就是说,user-service是服务,一个服务可以包含多个集群,如杭州、上海,每个集群下可以有多个实例,形成分级模型,如图:

微服务互相访问时,应该尽可能访问同集群实例,因为本地访问速度更快。当本集群内不可用时,才访问其它集群。例如:

杭州机房内的order-service应该优先访问同机房的user-service。
修改user-service的application.yml文件,添加集群配置:
- spring:
- cloud:
- nacos:
- server-addr: localhost:8848
- discovery:
- cluster-name: HZ # 集群名称
重启两个user-service实例后,我们可以在nacos控制台看到下面结果:

我们再次复制一个user-service启动配置,添加属性:
-Dserver.port=8083 -Dspring.cloud.nacos.discovery.cluster-name=SH
启动UserApplication3后再次查看nacos控制台:

默认的ZoneAvoidanceRule并不能实现根据同集群优先来实现负载均衡。
因此Nacos中提供了一个NacosRule的实现,可以优先从同集群中挑选实例。
1)给order-service配置集群信息
修改order-service的application.yml文件,添加集群配置:
- spring:
- cloud:
- nacos:
- server-addr: localhost:8848
- discovery:
- cluster-name: HZ # 集群名称
2)修改负载均衡规则
修改order-service的application.yml文件,修改负载均衡规则:
- userservice:
- ribbon:
- NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
实际部署中会出现这样的场景:
服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求。
但默认情况下NacosRule是同集群内随机挑选,不会考虑机器的性能问题。
因此,Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高。
在nacos控制台,找到user-service的实例列表,点击编辑,即可修改权重:

在这里插入图片描述
在弹出的编辑窗口,修改权重:

注意:如果权重修改为0,则该实例永远不会被访问
Nacos提供了namespace来实现环境隔离功能。

默认情况下,所有service、data、group都在同一个namespace,名为public:

我们可以点击页面新增按钮,添加一个namespace:

然后,填写表单:

就能在页面看到一个新的namespace:

给微服务配置namespace只能通过修改配置来实现。
例如,修改order-service的application.yml文件:
- spring:
- cloud:
- nacos:
- server-addr: localhost:8848
- discovery:
- cluster-name: HZ
- namespace: 492a7d5d-237b-46a1-a99a-fa8e98e4b0f9 # 命名空间,填ID
重启order-service后,访问控制台,可以看到下面的结果:

在这里插入图片描述

此时访问order-service,因为namespace不同,会导致找不到userservice,控制台会报错:

Nacos的服务实例分为两种l类型:
配置一个服务实例为永久实例:
- spring:
- cloud:
- nacos:
- discovery:
- ephemeral: false # 设置为非临时实例
Nacos和Eureka整体结构类似,服务注册、服务拉取、心跳等待,但是也存在一些差异:
Nacos与Eureka的区别

Nacos一方面可以将配置集中管理,另一方可以在配置变更时,及时通知微服务,实现配置的热更新。
如何在nacos中管理配置呢?

然后在弹出的表单中,填写配置信息:

注意:项目的核心配置,需要热更新的配置才有放到nacos管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好。
微服务要拉取nacos中管理的配置,并且与本地的application.yml配置合并,才能完成项目启动。
但如果尚未读取application.yml,又如何得知nacos地址呢?
因此spring引入了一种新的配置文件:bootstrap.yaml文件,会在application.yml之前被读取,流程如下:

1)引入nacos-config依赖
首先,在user-service服务中,引入nacos-config的客户端依赖:
- <!--nacos配置管理依赖-->
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
- </dependency>
2)添加bootstrap.yaml
然后,在user-service中添加一个bootstrap.yaml文件,内容如下:
- spring:
- application:
- name: userservice # 服务名称
- profiles:
- active: dev #开发环境,这里是dev
- cloud:
- nacos:
- server-addr: localhost:8848 # Nacos地址
- config:
- file-extension: yaml # 文件后缀名
这里会根据spring.cloud.nacos.server-addr获取nacos地址,再根据
spring.application.name−{spring.profiles.active}.
本例中,就是去读取userservice-dev.yaml:

3)读取nacos配置
在user-service中的UserController中添加业务逻辑,读取pattern.dateformat配置:

在页面访问,可以看到效果:

我们最终的目的,是修改nacos中的配置后,微服务中无需重启即可让配置生效,也就是配置热更新。
要实现配置热更新,可以使用两种方式:
在@Value注入的变量所在类上添加注解@RefreshScope:

使用@ConfigurationProperties注解代替@Value注解。
在user-service服务中,添加一个类,读取patterrn.dateformat属性:
- package cn.itcast.user.config;
-
- import lombok.Data;
- import org.springframework.boot.context.properties.ConfigurationProperties;
- import org.springframework.stereotype.Component;
-
- @Component
- @Data
- @ConfigurationProperties(prefix = "pattern")
- public class PatternProperties {
- private String dateformat;
- }
@ConfigurationProperties是 Spring Boot 提供的一个注解,用于将配置文件中的属性绑定到一个 Java 类上。这样,我们可以方便地在代码中使用这些属性,而不需要手动解析配置文件。使用@ConfigurationProperties注解时,需要在类上添加该注解,并指定配置文件的前缀。
在UserController中使用这个类代替@Value:

有很多的业务相关参数,将来可能会根据实际情况临时调整。例如购物车业务,购物车数量有一个上限,默认是10,对应代码如下:

现在这里购物车是写死的固定值,我们应该将其配置在配置文件中,方便后期修改。
但现在的问题是,即便写在配置文件中,修改了配置还是需要重新打包、重启服务才能生效。能不能不用重启,直接生效呢?
这就要用到Nacos的配置热更新能力了,分为两步:
在Nacos中添加配置
在微服务读取配置
首先,我们在nacos中添加一个配置文件,将购物车的上限数量添加到配置中:

注意文件的dataId格式:
[服务名]-[spring.active.profile].[后缀名]
文件名称由三部分组成:
服务名:我们是购物车服务,所以是cart-service
spring.active.profile:就是spring boot中的spring.active.profile,可以省略,则所有profile共享该配置
后缀名:例如yaml
这里我们直接使用cart-service.yaml这个名称,则不管是dev还是local环境都可以共享该配置。
配置内容如下:
- hm:
- cart:
- maxAmount: 1 # 购物车商品数量上限
提交配置,在控制台能看到新添加的配置:

接着,我们在微服务中读取配置,实现配置热更新。
在cart-service中新建一个属性读取类:

代码如下:
- package com.hmall.cart.config;
-
- import lombok.Data;
- import org.springframework.boot.context.properties.ConfigurationProperties;
- import org.springframework.stereotype.Component;
-
- @Data
- @Component
- @ConfigurationProperties(prefix = "hm.cart")
- public class CartProperties {
- private Integer maxAmount;
- }
接着,在业务中使用该属性加载类:

不过,现在依然还有几个问题需要解决:
网关路由在配置文件中写死了,如果变更必须重启微服务
某些业务配置在配置文件中写死了,每次修改都要重启服务
每个微服务都有很多重复的配置,维护成本高
这些问题都可以通过统一的配置管理器服务解决。而Nacos不仅仅具备注册中心功能,也具备配置管理的功能:

微服务共享的配置可以统一交给Nacos保存和管理,在Nacos控制台修改配置后,Nacos会将配置变更推送给相关的微服务,并且无需重启即可生效,实现配置热更新。
网关的路由同样是配置,因此同样可以基于这个功能实现动态路由功能,无需重启网关即可修改路由配置。
以cart-service为例,我们看看有哪些配置是重复的,可以抽取的:
首先是jdbc相关配置:

然后是日志配置:

然后是swagger以及OpenFeign的配置

我们在nacos控制台分别添加这些配置。
首先是jdbc相关配置,在配置管理->配置列表中点击+新建一个配置:

在弹出的表单中填写信息:

其中详细的配置如下:
- spring:
- datasource:
- url: jdbc:mysql://${hm.db.host:192.168.150.101}:${hm.db.port:3306}/${hm.db.database}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
- driver-class-name: com.mysql.cj.jdbc.Driver
- username: ${hm.db.un:root}
- password: ${hm.db.pw:123}
- mybatis-plus:
- configuration:
- default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
- global-config:
- db-config:
- update-strategy: not_null
- id-type: auto
注意这里的jdbc的相关参数并没有写死,例如:
数据库ip:通过${hm.db.host:192.168.150.101}配置了默认值为192.168.150.101,同时允许通过${hm.db.host}来覆盖默认值
数据库端口:通过${hm.db.port:3306}配置了默认值为3306,同时允许通过${hm.db.port}来覆盖默认值
数据库database:可以通过${hm.db.database}来设定,无默认值
然后是统一的日志配置,命名为shared-log.yaml,配置内容如下:
- logging:
- level:
- com.hmall: debug
- pattern:
- dateformat: HH:mm:ss:SSS
- file:
- path: "logs/${spring.application.name}"
然后是统一的swagger配置,命名为shared-swagger.yaml,配置内容如下:
- knife4j:
- enable: true
- openapi:
- title: ${hm.swagger.title:黑马商城接口文档}
- description: ${hm.swagger.description:黑马商城接口文档}
- email: ${hm.swagger.email:zhanghuyi@itcast.cn}
- concat: ${hm.swagger.concat:虎哥}
- url: https://www.itcast.cn
- version: v1.0.0
- group:
- default:
- group-name: default
- api-rule: package
- api-rule-resources:
- - ${hm.swagger.package}
注意,这里的swagger相关配置我们没有写死,例如:
title:接口文档标题,我们用了${hm.swagger.title}来代替,将来可以有用户手动指定
email:联系人邮箱,我们用了${hm.swagger.email:zhanghuyi@itcast.cn},默认值是zhanghuyi@itcast.cn,同时允许用户利用${hm.swagger.email}来覆盖。
接下来,我们要在微服务拉取共享配置。将拉取到的共享配置与本地的application.yaml配置合并,完成项目上下文的初始化。
不过,需要注意的是,读取Nacos配置是SpringCloud上下文(ApplicationContext)初始化时处理的,发生在项目的引导阶段。然后才会初始化SpringBoot上下文,去读取application.yaml。
也就是说引导阶段,application.yaml文件尚未读取,根本不知道nacos 地址,该如何去加载nacos中的配置文件呢?
SpringCloud在初始化上下文的时候会先读取一个名为bootstrap.yaml(或者bootstrap.properties)的文件,如果我们将nacos地址配置到bootstrap.yaml中,那么在项目引导阶段就可以读取nacos中的配置了。
因此,微服务整合Nacos配置管理的步骤如下:
1)引入依赖:
在cart-service模块引入依赖:
-
-
-
com.alibaba.cloud -
spring-cloud-starter-alibaba-nacos-config -
-
-
-
org.springframework.cloud -
spring-cloud-starter-bootstrap -
2)新建bootstrap.yaml
在cart-service中的resources目录新建一个bootstrap.yaml文件:
内容如下:
- spring:
- application:
- name: cart-service # 服务名称
- profiles:
- active: dev
- cloud:
- nacos:
- server-addr: 192.168.150.101 # nacos地址
- config:
- file-extension: yaml # 文件后缀名
- shared-configs: # 共享配置
- - dataId: shared-jdbc.yaml # 共享mybatis配置
- - dataId: shared-log.yaml # 共享日志配置
- - dataId: shared-swagger.yaml # 共享日志配置
3)修改application.yaml
由于一些配置挪到了bootstrap.yaml,因此application.yaml需要修改为:
- server:
- port: 8082
- feign:
- okhttp:
- enabled: true # 开启OKHttp连接池支持
- hm:
- swagger:
- title: 购物车服务接口文档
- package: com.hmall.cart.controller
- db:
- database: hm-cart
重启服务,发现所有配置都生效了。
当nacos、服务本地同时出现相同属性时,优先级有高低之分:
