对于微服务而言配置本地化是个很大的鸡肋,不可能每次需要改个配置都要重新把服务重新启动一遍,因此最终的解决方案都是将配置外部化,托管在一个平台上达到不用重启服务即可一次修改多处生效的目的。
但是对于单体应用的Spring Boot项目而言,动态刷新显然是有点多余,反正就一个服务,改下重启不就行了,然而在某些特殊的场景下还是必须用到动态刷新的
微服务下有哪几种主流的方案?
微服务下的动态配置中心有三种主流的方式,如下图:
上图中的三种配置中心方案可以说是现在企业中使用率最高的,分别是:
-
Nacos:阿里巴巴的最近开源的项目,这个家伙很牛逼,一个干掉了
Eureka
(停更)和
Config+Bus
,既能作为配置中心也能作为注册中心,并且有自己的独立的 管理平台,可以说是现在最主流的一种。
-
Config+Bus:早期在用的微服务配置中心,可以依托
GitHub
管理微服务的配置文件,这种现在也是有不少企业在用,但是需要自己独立部署一个微服务,和
Nacos
相比逊色了不少。
-
Apollo:携程开源项目Apollo,这个也是不少企业在用,陈某了解的不多,有兴趣的可以深入研究下。
针对Spring Boot 适用的几种方案?
其实上述三种都可以在Spring Boot项目中适配,但是作为单体应用有些重了。
1. 如果项目是一个微服务的集群,业务功能复杂
建议: Spring Boot+Nacos
2. 如果项目是单体服务,业务功能单一
建议: Spring Boot+Config+actuator
Spring Boot+手动refresh
针对Spring Boot+Nacos
阿里要做的其实是一个微服务生态,Nacos不仅仅可以作为Spring Cloud的配置和注册中心,也适配了Dubbo、K8s,官方文档中对于如何适配都做了详细的介绍,作者 这里就不再详细介绍了,如下图:
当然Nacos对Spring、Spring Boot 项目同样适用。
如何使用呢?这里作者只提供下思路,不做过多的深究,这篇在作者下个专栏Spring Cloud 进阶会详细介绍:
-
下载对应版本的Nacos,启动项目,访问
http://localhost:8848
进入Nacos的管理界面;
-
Spring Boot 项目引入Nacos的配置依赖
nacos-config-spring-boot-starter
,配置Nacos管理中心的地址。
-
@NacosPropertySource
、
@NacosValue
两个注解结合完成。
-
如果公司项目做了后台管理,则可以直接调用Nacos开放的API修改对应配置的值(替代了Nacos管理界面的手动操作),API的地址:https://nacos.io/zh-cn/docs/open-api.html
针对Spring Boot+Config+actuator
1. 添加Config的依赖,如下
<!-- springCloud的依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- config的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- actuator的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2. 配置文件中暴露Spring Boot的端点,如下:
management.endpoints.web.exposure.include=*
3. 配置文件中新增三个属性配置
config.version=22
config.app.name=dynamic-project
config.platform=mysql
4. 结合
@RefreshScope
注解动态刷新,写个Controller,如下:
@RestController
//@RefreshScope该注解必须标注,否则无法完成动态更新
@RefreshScope
public class DynamicConfigController {
@Value("${config.version}")
private String version;
@Value("${config.app.name}")
private String appName;
@Value("${config.platform}")
private String platform;
@GetMapping("/show/version")
public String test(){
return "version="+version+"-appName="+appName+"-platform="+platform;
5. 启动项目测试
1. 浏览器访问http://localhost:8080/show/version
2. 修改target目录下的配置文件,如下:
config.version=33
config.app.name=dynamic-project
config.platform=ORACLE
3. POST请求http://localhost:8080/actuator/refresh接口,手动刷新下配置(必须,否则不能自动刷新)
4. 浏览器再次输入http://localhost:8080/show/version
可以看到,配置已经自动修改了
针对Spring Boot+手动refresh
经过源码分析,配置刷新是发布刷新事件,在监听器实现刷新
发布事件:
以nacos为例 : com.alibaba.cloud.nacos.refresh.NacosContextRefresher
applicationContext.publishEvent(new RefreshEvent(this,null,"Refresh Nacos config"));
监听刷新事件: org.springframework.cloud.endpoint.event.RefreshEventListener
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationReadyEvent) {
handle((ApplicationReadyEvent) event);
else if (event instanceof RefreshEvent) {
handle((RefreshEvent) event);
public void handle(ApplicationReadyEvent event) {
this.ready.compareAndSet(false, true);
执行刷新 : org.springframework.cloud.context.refresh.ContextRefresher
public synchronized Set<String> refresh() {
Set<String> keys = refreshEnvironment();
this.scope.refreshAll();
return keys;
public synchronized Set<String> refreshEnvironment() {
Map<String, Object> before = extract(
this.context.getEnvironment().getPropertySources());
addConfigFilesToEnvironment();
Set<String> keys = changes(before,
extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
return keys;
可以基于刷新自己实现动态加载
@GetMapping("/show/refresh")
public String refresh(){
//修改配置文件中属性
HashMap<String, Object> map = new HashMap<>();
map.put("config.version",99);
map.put("config.app.name","appName");
map.put("config.platform","ORACLE");
MapPropertySource propertySource=new MapPropertySource("dynamic",map);
//将修改后的配置设置到environment中
environment.getPropertySources().addFirst(propertySource);
//异步调用refresh方法,避免阻塞一直等待无响应
new Thread(() -> contextRefresher.refresh()).start();
return "success";
1. 浏览器访问http://localhost:8080/show/version
2. 修改target目录下的配置文件,如下:
config.version=33
config.app.name=dynamic-project
config.platform=ORACLE
3. POST请求http://localhost:8080/actuator/refresh接口,手动刷新下配置(必须,否则不能自动刷新)
4. 浏览器再次输入http://localhost:8080/show/version
可以看到,配置已经自动修改了
由于时间关系,仅记录要点:
1.引入 spring-boot-starter-actuator及spring-cloud-starter-config
2.对需要刷新的属性使用@Value注解,同时将类使用@RefreshScope注解进行标记
import org.springframework.beans.factory.annotation.Value;
import org.springf...
比如:在上面的配置文件中,有个version的配置,当我想动态的更改这个name的值的时候,如果在代码里更改然后打包也是可行的方法,但是如果更改频繁,那么就麻烦了。
方案演示,实际以个人情况为准:
spring boot 配置文件动态刷新说明实现方式1.建立一个spring boot工程,名称:boot-config,目录如下:2.引入pom依赖application.yml文件TestController示例测试1.启动项目,直接访问:localhost:8080/hello2.找到项目编译文件夹,找到配置文件,修改hello的值:保存文件,访问配置文件刷新接口:http://localhost:8080/actuator/refresh再次访问:localhost:8080/hello:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
application.yml