一、使用场景
需要根据配置文件中的某个配置生成指定的容器。原先是每次启动时加载指定的配置项后生成。但是这种方式的缺点很明显,每次需要增删配置时,都需要重启才可以,不能做到有效的动态增删,对于线上项目就很难受。
二、原理思路
通过consul线上配置,来实现动态加载配置,这里使用consul做案例。
原理:使用consul作为配置中心,通过consul自带得config watch(配置监听功能)和 Springboot的事件监听事件来实现。
流程是:consul开启watch后,会定期检查指定的配置文件,如果有更改,触发RefreshEvent事件,然后通过触发事件可以获取到更改的配置文件,然后可以实现热加载!这些可以通过源码看到,感兴趣可以看看。
三、配置中心-代码实现
- 亘古不变,万事先依赖
这里注意一下consul配置中心时对springboot版本的一些要求,特殊情况代码里我写的有注释!
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-config</artifactId>
<version>3.1.3</version>
</dependency>
<!-- 如果使用的是springbooit2.4以后及bootsrap.yml启动的,报错找不到启动项的,需要添加以下依赖-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-bootstrap</artifactId>-->
<!-- <version>3.0.3</version>-->
<!-- </dependency>-->
- 其次,在application.yml或bootstrap.yml上写入配置文件:
spring:
cloud:
consul:
port: 8500
host: localhost
config:
prefix: config #consul key/value对应的前缀,就是文件夹
format: files
watch:
enabled: true #是否开启监听
waitTime: 1 #检查consul文件的事件间隔
data-key: ${spring.application.name} #对应的配置文件名称
config:
import: 'consul:'
application:
name: cktest
以上文件对应的就是consul,key/value下面的:config/你的data-key.yaml文件。
注意:
这里的config:import比较特殊,如果是springboot2.4之前的可以不用添加,sringboot2.4之后必须添加。
如果是bootstrap.yml,则需要添加上面注释的maven,否则会报错找不到config:import,也可以改成application.yml即可。原因:因为springboot2.4之后好像去除bootstrap了,所以需要引入bootstrap以来才行。
-
然后在consul上对应的位置,创建好对应的yaml文件及写入其他项目配置。
-
此后启动项目,如果端口是8092,则说明consul配置中心成功了。
四、配置热加载-代码实现
- 实现SmartApplicationListener接口
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.cloud.endpoint.event.RefreshEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.core.env.Environment;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@Configuration
public class ConfigChangeListener implements SmartApplicationListener {
private ApplicationContext applicationContext;
private ContextRefresher refresh;
private AtomicBoolean ready = new AtomicBoolean(false);
//注入刷新事件和程序上下文
@Autowired
public ConfigChangeListener(ContextRefresher refresh, ApplicationContext applicationContext) {
this.refresh = refresh;
this.applicationContext = applicationContext;
}
//是否支持的事件类型
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return ApplicationReadyEvent.class.isAssignableFrom(eventType)
|| RefreshEvent.class.isAssignableFrom(eventType);
}
//初始化和配置更改时触发的事件
@Override
public void onApplicationEvent(ApplicationEvent event) {
try {
//项目启动时会触发该事件
if (event instanceof ApplicationReadyEvent) {
handle((ApplicationReadyEvent) event);
} else if (event instanceof RefreshEvent) {
//consul配置文件更改时触发改事件
handle((RefreshEvent) event);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 监听ApplicationReadyEvent事件,当Spring容器初始化完成后,会触发该事件
* */
public void handle(ApplicationReadyEvent event) {
Environment environment = applicationContext.getEnvironment();
try {
String property = environment.getProperty("server.port");
System.out.println("haha" + property);
String property2 = environment.getProperty("lee.test");
System.out.println("haha" + property2);
} catch (Exception e) {
throw new RuntimeException(e);
}
this.ready.compareAndSet(false, true);
}
/*
* 监听RefreshEvent事件,当配置文件发生变更时,会触发该事件
* */
public void handle(RefreshEvent event) throws Exception {
// 程序未初始化好之前不做处理
if (this.ready.get()) {
log.info("触发事件 " + event.getEventDesc());
//调用ContextRefresher刷新配置方法
//刷新方法执行后,才会获得最新的配置
Set<String> keys = this.refresh.refresh();
log.info("更改的配置有: " + keys);
Environment environment = null;
if (keys.size() > 0) {
environment = applicationContext.getEnvironment();
}
for (String key : keys) {
String property = environment.getProperty(key);
System.out.println(property);
}
}
}
}
- 更改consul上的配置文件
当输出的是最新的配置值,说明热加载成功!
五、总结
热加载需要使用consul或者nacos作为配置中心,然后通过配置文件的监控,实现热加载!
🙉在小小的电脑上面敲呀敲呀敲,写短短的代码,埋小小的坑🙈
🙉在大大的电脑上面敲呀敲呀敲,写大大的代码,埋大大的坑🙈
🙉在特别大的电脑上面敲呀敲呀敲,写特别大的代码,埋特别大的坑🙈
🙉优秀的你肯定是一个不爱写Bug并且爱点赞关注的靓仔吧!🙈