在Nacos中,可以做配置给服务做配置管理,在现实中,我们需要更新某个在运行中的服务的配置时,如果将服务停下来修改了配置在重启,对于在生产环境中的服务可能影响比较大,对此,Nacos对配置进行管理,还可以进行热更新,实时的刷新到运行的服务中。
案例
在user-service
服务中配置了日期时间的格式为yyyy-MM-dd HH:mm:ss
,想不修改本地的配置文件的情况下,修改日期时间的格式为yyyy年MM月dd日 HH:mm:ss
,在Nacos中如何实现,就要涉及到它的统一配置管理。
统一配置管理
通过以上的图片可以看出,在服务启动的时候,会同时去将本地的application.yml配置和Nacos中的配置文件都读取出来,然后再进行后续的操作。但是,服务是如何知道Nacos的信息以及Nacos里面的配置信息的呢?而且我们对于Nacos的配置还在本地的application.yml文件中,难道是先加载本地的配置文件,先读取到Nacos的信息,再去Nacos中读取配置信息吗?
其实不是的,我们都知道,在spring的配置文件中,还有一个配置文件比application.yml的级别要高,那就是bootstrap.yml,它会比application.yml先被读取到,所以,对于以上的问题,就可以解决了,我么将Nacos的信息配置到bootstrap.yml文件中,就可以先读取到Nacos的信息,在根据这些信息去读取Nacos中的配置信息,然后再将其和本地的合并起来即可。
Nacos中添加配置
添加依赖
在需要进行配置管理的服务的pom中添加Nacos的配置依赖支持,这里我们为
user-service
添加。
<!-- nacos配置管理依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
在userservice中的resource目录添加一个bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml
bootstrap.yml
spring:
application:
name: userservice
profiles:
active: dev
cloud:
nacos:
server-addr: localhost:8848
config:
file-extension: yaml
namespace:
application.yml,在本地中,我们没有多配制其他的
server:
port: 8081
可以发现,bootstrap.yml中的name和active,以及file-extension,刚好可以组成我们在Nacos中的配置文件的名称userservice-dev.yaml
测试
我们在Controller中写一个可以获取打印配置的接口:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Value("${pattern.dateformat}")
private String dateformat;
@GetMapping("/getdate")
public String getdate(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
}
}
启动user-service
服务,请求接口结果,可见配置已经生效
热更新
在上述中,我们实现了Nacos的配置管理,并且已经生效,但是,现在尝试在Nacos中修改配置:
再去访问之前的接口,发现并没有生效,那该如何实现配置的热更新呢?
有两种方式可以实现:
方式一:@RefreshScope
在我们使用到了Nacos的代码中的类上,配置@RefreshScope
注解,即可实现配置的热更新:
@RestController
@RequestMapping("/user")
@RefreshScope
public class UserController {
@Autowired
private UserService userService;
@Value("${pattern.dateformat}")
private String dateformat;
@GetMapping("/getdate")
public String getdate(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
}
}
方式二:定义配置类
可以将需要在Nacos中配置的属性,定义成一个类,就以上的案例,我们可以这样定义:
@Data
@Component
@ConfigurationProperties(prefix = "pattern")//使用pattern前缀,可以将前缀为pattern的配置加载到类中,并自动按命名匹配
public class PatternProperties {
private String dateformat;
}
那此时的接口代码就应该如下写了,需要将我们定义的配置类,注入到需要使用的地方:
@RestController
@RequestMapping("/user")
//@RefreshScope//此时我们就不需要这个注解了
public class UserController {
@Autowired
private UserService userService;
@Autowired
PatternProperties properties;
@GetMapping("/getdate")
public String getdate(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(properties.getDateformat()));
}
}
这样的方式,同样可以实现配置的热更新。
相同微服务的多环境配置共享
其实,微服务启动时会从nacos读取多个配置文件:
[spring.application.name]-[spring.profiles.active].yaml
,例如:userservice-dev.yaml
[spring.application.name].yaml
,例如:userservice.yaml
无论profile如何变化,[spring.application.name].yaml
这个文件一定会加载,因此多环境共享配置可以写入这个文件。
在Nacos中,在此添加一个配置文件userservice.yml
然后我们在接口中再定义一个访问共享属性的接口:
PatternProperties.java
@Data
@Component
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
private String dateformat;
private String enSharedValue;
}
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
PatternProperties properties;
@GetMapping("/getdate")
public String getdate(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(properties.getDateformat()));
}
@GetMapping("/properties")
public PatternProperties getenshard(){
return properties;
}
}
然后,在我们启动的时候,多启动一个环境为test的实例:
访问:
为什么8082端口的dateformat是空的?因为我们在Nacos中根本没有配置userservice-test.yml
的信息,所以获取不到,只能获取到共享的配置userservice.yml
相同微服务的多种配置的优先级
不同微服务的环境共享
首先创建一个common.yaml
代表不同微服务的共享配置文件
同时,在每一个微服务下面创建自己的bootstrap.yml配置文件,并设置共享的配置文件
然后再自己的接口中定义访问共享属性的接口:
测试,可以看到,两个不同的微服务都加载了common.yaml
共享配置文件
除此配置方式之外,还有一种非常类似的方式,就是将bootstrap.yml中的shared-configs换成extension-configs即可: