Spring Cloud Consul Config
依赖
- 加入 spring-cloud-starter-consul-config
启用
- 在 bootstrap.properties | yml 中 加入相关配置
• spring.cloud.consul.host=localhost
• spring.cloud.consul.port=8500
• spring.cloud.consul.config.enabled=true
开启consul配置中心支持
Consul 中的数据怎么存
配置项
- spring.cloud.consul.config.format=
KEY_VALUE | YAML | PROPERTIES | FILES
配置数据存储的形式 - /config/应用名,profile/data
所有的配置 在这个data节点里面 - /config/application,profile/data
如何定制
-
spring.cloud.consul.config.data-key=data
配置key的名字,由于Consul是K/V存储,配置存储在对应K的V中 -
spring.cloud.consul.config.root=config
-
spring.cloud.consul.config.default-context=application
指定consul配置的配置文件父路径 -
spring.cloud.consul.config.profile-separator=’,’
置配置的分隔符
配置项变更
自动刷新配置
-
spring.cloud.consul.config.watch.enabled=true
-
spring.cloud.consul.config.watch.delay=1000
刷新时间为每隔一秒钟
实现原理
- 单线程 ThreadPoolTaskScheduler
- ConsulConfigAutoConfiguration.CONFIG_WATCH_TASK_SCHEDULER_NAME
使用 ThreadPoolTaskScheduler 每隔一秒钟 去检测 看看有没有发生变化 如果有变化 就发一个event事件出来
实例
需要修改的代码
bootstrap.properties
spring.application.name=waiter-service
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.prefer-ip-address=true
spring.cloud.consul.config.enabled=true
#是否启用配置中心功能
spring.cloud.consul.config.format=yaml
#设置配置值的格式
pom文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>0.14.1</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-money</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.jadira.usertype</groupId>
<artifactId>usertype.core</artifactId>
<version>6.0.1.GA</version>
</dependency>
<!-- 增加Jackson的Hibernate类型支持 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
结果分析
启动 consul
输入 http://localhost:8500 进入 consul 在 key/Value 中 新建一个 key/Value 为yaml格式
启动程序 使用 postman 进行订单的创建 我们之前在consul写入的配置 成功 配置
将 折扣进行调整 变成 50 控制台显示discount的变更
使用 postman 继续进行测试 结果中的discount已经成功修改
源码分析
ConsulConfigBootstrapConfiguration
@Configuration
@ConditionalOnConsulEnabled
public class ConsulConfigBootstrapConfiguration {
public ConsulConfigBootstrapConfiguration() {
}
@Configuration
@EnableConfigurationProperties
@Import({ConsulAutoConfiguration.class})
@ConditionalOnProperty(
name = {"spring.cloud.consul.config.enabled"},
matchIfMissing = true
)
protected static class ConsulPropertySourceConfiguration {
@Autowired
private ConsulClient consul;
protected ConsulPropertySourceConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public ConsulConfigProperties consulConfigProperties() {
return new ConsulConfigProperties();
}
@Bean //配置 consulPropertySourceLocator 通过 consulPropertySourceLocator 找到对应的 PropertySource
public ConsulPropertySourceLocator consulPropertySourceLocator(ConsulConfigProperties consulConfigProperties) {
return new ConsulPropertySourceLocator(this.consul, consulConfigProperties);
}
}
}
ConsulConfigAutoConfiguration
@Configuration
@ConditionalOnConsulEnabled
@ConditionalOnProperty(
name = {"spring.cloud.consul.config.enabled"},
matchIfMissing = true
)
public class ConsulConfigAutoConfiguration {
public static final String CONFIG_WATCH_TASK_SCHEDULER_NAME = "configWatchTaskScheduler";
public ConsulConfigAutoConfiguration() {
}
@Configuration
@ConditionalOnClass({RefreshEndpoint.class})
protected static class ConsulRefreshConfiguration {
protected ConsulRefreshConfiguration() {
}
@Bean
@ConditionalOnProperty(
name = {"spring.cloud.consul.config.watch.enabled"},
matchIfMissing = true
)
//在 configWatch 中 会用到一个单线程的 ThreadPoolTaskScheduler 在创建的时候 将 name 这个常量传入
public ConfigWatch configWatch(ConsulConfigProperties properties, ConsulPropertySourceLocator locator, ConsulClient consul, @Qualifier("configWatchTaskScheduler") TaskScheduler taskScheduler) {
return new ConfigWatch(properties, consul, locator.getContextIndexes(), taskScheduler);
}
@Bean(
name = {"configWatchTaskScheduler"}
)
@ConditionalOnProperty(
name = {"spring.cloud.consul.config.watch.enabled"},
matchIfMissing = true
)
public TaskScheduler configWatchTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
}
}
ConfigWatch
public class ConfigWatch implements ApplicationEventPublisherAware, SmartLifecycle {
private static final Log log = LogFactory.getLog(ConfigWatch.class);
private final ConsulConfigProperties properties;
private final ConsulClient consul;
private final TaskScheduler taskScheduler;
private final AtomicBoolean running;
private LinkedHashMap<String, Long> consulIndexes;
private ApplicationEventPublisher publisher;
private boolean firstTime;
private ScheduledFuture<?> watchFuture;
public ConfigWatch(ConsulConfigProperties properties, ConsulClient consul, LinkedHashMap<String, Long> initialIndexes) {
this(properties, consul, initialIndexes, getTaskScheduler());
}
public ConfigWatch(ConsulConfigProperties properties, ConsulClient consul, LinkedHashMap<String, Long> initialIndexes, TaskScheduler taskScheduler) {
this.running = new AtomicBoolean(false);
this.firstTime = true;
this.properties = properties;
this.consul = consul;
this.consulIndexes = new LinkedHashMap(initialIndexes);
this.taskScheduler = taskScheduler;
}
private static ThreadPoolTaskScheduler getTaskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.initialize();
return taskScheduler;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void start() {//在启动的时候 设置它的时间 从 properties 中 找出时间的参数(有默认的)
if (this.running.compareAndSet(false, true)) {
this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(this::watchConfigKeyValues, (long)this.properties.getWatch().getDelay());
}
}
public boolean isAutoStartup() {
return true;
}
public void stop(Runnable callback) {
this.stop();
callback.run();
}
public int getPhase() {
return 0;
}
public void stop() {
if (this.running.compareAndSet(true, false) && this.watchFuture != null) {
this.watchFuture.cancel(true);
}
}
public boolean isRunning() {
return this.running.get();
}
@Timed("consul.watch-config-keys")
public void watchConfigKeyValues() {//是一个定时任务 每隔一秒钟 会跑一次 在这里面 做了一个 从 Consul 中获取值的动作
if (this.running.get()) {
Iterator var1 = this.consulIndexes.keySet().iterator();
while(var1.hasNext()) {
String context = (String)var1.next();
if (this.properties.getFormat() != Format.FILES && !context.endsWith("/")) {
context = context + "/";
}
try {
Long currentIndex = (Long)this.consulIndexes.get(context);
if (currentIndex == null) {
currentIndex = -1L;
}
log.trace("watching consul for context '" + context + "' with index " + currentIndex);
String aclToken = this.properties.getAclToken();
if (StringUtils.isEmpty(aclToken)) {
aclToken = null;
}
Response<List<GetValue>> response = this.consul.getKVValues(context, aclToken, new QueryParams((long)this.properties.getWatch().getWaitTime(), currentIndex));//取得kv的值 将取回来的值 进行判断 是否发生看变化 如果 变了 就publish一个RefreshEvent消息出去 推出去之后 我们就去刷新 这里 所有需要的refreshsource 加了source的这些bean 它好会被刷新掉
if (response.getValue() != null && !((List)response.getValue()).isEmpty()) {
Long newIndex = response.getConsulIndex();
if (newIndex != null && !newIndex.equals(currentIndex)) {
if (!this.consulIndexes.containsValue(newIndex) && !currentIndex.equals(-1L)) {
log.trace("Context " + context + " has new index " + newIndex);
ConfigWatch.RefreshEventData data = new ConfigWatch.RefreshEventData(context, currentIndex, newIndex);
this.publisher.publishEvent(new RefreshEvent(this, data, data.toString()));
} else if (log.isTraceEnabled()) {
log.trace("Event for index already published for context " + context);
}
this.consulIndexes.put(context, newIndex);
} else if (log.isTraceEnabled()) {
log.trace("Same index for context " + context);
}
} else if (log.isTraceEnabled()) {
log.trace("No value for context " + context);
}
} catch (Exception var8) {
if (this.firstTime && this.properties.isFailFast()) {
log.error("Fail fast is set and there was an error reading configuration from consul.");
ReflectionUtils.rethrowRuntimeException(var8);
} else if (log.isTraceEnabled()) {
log.trace("Error querying consul Key/Values for context '" + context + "'", var8);
} else if (log.isWarnEnabled()) {
log.warn("Error querying consul Key/Values for context '" + context + "'. Message: " + var8.getMessage());
}
}
}
}
this.firstTime = false;
}
static class RefreshEventData {
private final String context;
private final Long prevIndex;
private final Long newIndex;
RefreshEventData(String context, Long prevIndex, Long newIndex) {
this.context = context;
this.prevIndex = prevIndex;
this.newIndex = newIndex;
}
public String getContext() {
return this.context;
}
public Long getPrevIndex() {
return this.prevIndex;
}
public Long getNewIndex() {
return this.newIndex;
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
ConfigWatch.RefreshEventData that = (ConfigWatch.RefreshEventData)o;
return Objects.equals(this.context, that.context) && Objects.equals(this.prevIndex, that.prevIndex) && Objects.equals(this.newIndex, that.newIndex);
} else {
return false;
}
}
public int hashCode() {
return Objects.hash(new Object[]{this.context, this.prevIndex, this.newIndex});
}
public String toString() {
return (new ToStringCreator(this)).append("context", this.context).append("prevIndex", this.prevIndex).append("newIndex", this.newIndex).toString();
}
}
}
ConsulConfigProperties
在这个里面 有默认的时间参数
public static class Watch {
private int waitTime = 55;
private boolean enabled = true;
private int delay = 1000;
转载:https://blog.csdn.net/weixin_43790623/article/details/105217987