使用 Nacos 的配置功能和自动更新
Nacos 不但可以作为注册中心,同时也可以作为配置中心,方便我们对 SpringCloud 中的各个 SpringBoot 微服务的配置进行统一的管理维护。尤其是当微服务数量较多,并且在不同的服务器上进行部署时,使用配置中心进行统一管理维护的优势就更加明显。
本篇博客仍然使用之前搭建的 Nacos 集群,并在上篇博客 Demo 的基础上进行简单的改造,实现微服务从 Nacos 中读取配置,并且在不重启微服务的前提下,实现微服务代码读取的配置内容,跟随 Nacos 配置的修改而自动更新。本篇博客只介绍关键核心内容,详情请查看源代码,在博客最后会提供源代码下载。
一、微服务的配置文件
首先看一下 Demo 的 SpringCloud 工程结构,如下图所示:
工程中有两个微服务 consumer_app 和 provider_app ,使用的服务名称分别为 consumer 和 provider 。
需要注意的是:微服务想要读取 Nacos 中的配置文件,配置文件的名称需要以微服务的名称来命名。因此我们需要在 Nacos 中添加两个配置文件:consumer.yaml 和 provider.yaml 。
当然并非所有的配置内容,都放到 nacos 中进行管理,这个可以根据项目的实际情况而定。以 consumer_app 为例,我们将其配置分别存放在 3 个配置文件中:本地的 application.yml 和 bootstrap.yml ,以及 Nacos 中的 consumer.yaml 。下面先看一下这 3 个配置文件的内容:
本地 bootstrap.yml 的内容如下:(spring 引入了 bootstrap.yaml 文件,会在 application.yml 之前被加载)
#微服务启动后,会优先加载 bootstrap.yml 文件,这里只配置了服务名称,以及nacos连接信息
#本地的 application.yml 也是会加载的,比如服务启动端口就配置在 application.yml 中。
spring:
application:
# 微服务的名称,这个很重要,因为需要到 nacos 中获取【微服务名称.yaml】的配置文件
name: consumer
cloud:
nacos:
# 配置 Nacos 的 ip 和 端口
server-addr: 192.168.216.129:80
# 配置登录的 账号 和 密码
username: nacos
password: nacos
config:
file-extension: yaml
本地 application.yml 的内容如下:
# 本地配置文件,在微服务启动时,也会被加载
server:
port: 8888
#针对具体的服务,配置使用 nacos 的负载均衡策略(随机策略)
#当前项目中接口提供者的服务名称是 provider
#如果去掉以下的配置,则默认使用 ribbon 的轮训策略
provider:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
logging:
level:
com.jobs: debug
Nacos 中的 consumer.yaml 的配置内容如下:
二、读取 Nacos 中的配置以及自动更新
1 父工程和子工程需要引入的依赖
父工程的 pom 文件中需要引入 SpringCloud Alibaba 的依赖:
<!--引入 springCloud alibaba 依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
需要注意版本之间的兼容性,否则可能会出问题。本篇博客中,使用的版本如下:
- SpringBoot 的版本为 2.3.12.RELEASE
- SpringCloud 版本为 Hoxton.SR10
- SpringCloud Alibaba 的版本为 2.2.9.RELEASE
这里以 consumer_app 子工程为例,需要的依赖如下:
<!--nacos配置管理依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2 使用 @Value 方式读取配置和自动更新
我们以读取 Nacos 中的 consumer.yaml 的 myvalue.dateformat 中的值为例,更改一下 ConsumerController 代码。添加一个新的接口,并在 Controller 上添加 @RefreshScope 注解,核心代码如下所示:
/*
如果使用 $Value 来获取 nacos 中配置文件的配置值,
当 nacos 中配置值修改后,要想让程序中获取的值也立刻更新,
就需要在类上添加 @RefreshScope 注解
*/
@RefreshScope
@RequestMapping("/consumer")
@RestController
public class ConsumerController {
@Value("${myvalue.dateformat}")
private String dateformat;
@GetMapping("/now")
public String getDateNow() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
}
}
3 使用 @ConfigurationProperties 的配置类和自动更新
我们以读取 Nacos 中的 consumer.yaml 的 myconfig 下的 name 和 age 的值为例,首先需要添加一个与 Nacos 中配置内容相对应的映射类,具体代码如下所示:
package com.jobs.configpojo;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "myconfig")
public class MyConfig {
private String name;
private Integer age;
}
新创建一个 ConfigTestController 和接口,读取配置的代码如下所示:
package com.jobs.controller;
import com.jobs.configpojo.MyConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/*
如果使用 @ConfigurationProperties 为注解的配置类映射 nacos 配置文件的配置信息,
那么当 nacos 中的配置值修改后,则程序中就能够自动获取到更新,这种方式更加方便
*/
@RequestMapping("/config")
@RestController
public class ConfigTestController {
@Autowired
private MyConfig myConfig;
@GetMapping("/getdata")
public MyConfig getMyConfig() {
return myConfig;
}
}
三、相同配置内容的优先级
在 Nacos 中除了【微服务名称.yaml】配置文件外,也可以添加【微服务名称-profile名称.yaml】配置文件,以 provider_app 为例,我们可以在 Nacos 中添加 2 个配置文件:provider.yaml 和 provider-dev.yaml。
我们可以把一些公用的配置放在【微服务名称.yaml】配置文件中,对于不同环境的中有差异的配置,可以放在【微服务名称-profile名称.yaml】配置文件中,如常用的环境为:开发环境 dev 、测试环境 test 、生成环境 pro 等等,这个名称可以自己随便取。
首先对于 bootstrap.yml 配置文件,由于其加载的时间最早,绝大多数情况下,我们只会将【微服务名称】、【profile 名称】、【Nacos 连接信息】配置在里面,不会配置其它额外的信息,对于 provider_app 微服务来说,其 bootstrap.yml 文件内容如下:
#微服务启动后,会优先加载 bootstrap.yml 文件,这里只配置了服务名称,以及nacos连接信息
#本地的 application.yml 也是会加载的,比如服务启动端口就配置在 application.yml 中。
spring:
application:
# 微服务的名称,这个很重要,因为需要到 nacos 中获取【微服务名称.yaml】的配置文件
name: provider
profiles:
#开发环境,比如可以使用 test 表示测试环境,pro 表示生产环境
active: dev
cloud:
nacos:
# 配置 Nacos 的 ip 和 端口
server-addr: 192.168.216.129:80
# 配置登录的 账号 和 密码
username: nacos
password: nacos
config:
file-extension: yaml
由于在 bootstrap.yml 文件中配置了 spring.profiles.active 为 dev ,因此 provider_app 微服务启动时,会加载这几个配置文件:本地的 application.yml 、Nacos 中的 provider.yaml 和 provider-dev.yaml ,下面列出这 3 个文件的相同名称的配置:
本地 application.yml 的配置内容:
server:
port: 9091
logging:
level:
com.jobs: debug
mytest:
name: 本地配置值
depart: 本地部门
Nacos 中添加的 provider.yaml 的配置内容:
Nacos 中添加的 provider-dev.yaml 的配置内容:
可以看到 3 个配置文件,都有 mytest 下的 name 和 depart 的配置,具体会使用哪个配置内容呢?
在本篇博客的代码中,使用了 @ConfigurationProperties 的配置类的方式读取以上配置,并提供的接口进行验证。首先新增一个与 Nacos 配置映射的配置类,具体细节如下所示:
package com.jobs.configpojo;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "mytest")
public class MyTest {
private String name;
private String depart;
}
然后更改了一下对外提供的接口,代码如下:
package com.jobs.controller;
import com.jobs.configpojo.MyTest;
import com.jobs.pojo.Employee;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.UnsupportedEncodingException;
import java.util.Random;
@Slf4j
@RequestMapping("/provider")
@RestController
public class ProviderController {
@Autowired
private MyTest myTest;
@GetMapping("/{id}")
public Employee getEmployeeById(@PathVariable("id") Integer id) {
Employee employee = new Employee();
employee.setId(id);
//使用配置文件中的配置值
//配置文件优先级:provider-dev.yaml > provider.yaml > application.yml
//你可以分别注释 nacos 中 provider-dev.yaml 和 provider.yaml 中的配置,进行验证结果
employee.setName(myTest.getName());
employee.setDepart(myTest.getDepart());
log.info("返回员工的 id 为:" + id);
return employee;
}
}
你可以分别 Nacos 中的 provider-dev.yaml 和 provider.yaml 中的配置注释和启用,进行验证,最终的结果为:
【微服务名称-profile名称.yaml】 > 【微服务名称.yaml】 > 【本地 application.yml】
本篇博客的源代码下载地址为:https://files.cnblogs.com/files/blogs/699532/nacos_config.zip