Consul
Consul是一款中间件,可提供KV存储功能,同时提供了创建、修改、查询KV存储的HTTP API,因此可作为配置中心。
Spring Cloud Consul
Spring Cloud Consul是基于Spring Cloud的公共接口,提供了集成Consul配置能力的微服务框架。
源码分析
ConfigWatch
这个类中提供了一个定时任务
@Override
public void start() {
if (this.running.compareAndSet(false, true)) {
this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
this::watchConfigKeyValues, this.properties.getWatch().getDelay());
}
}
该方法定时执行(getDelay():每当上一次执行完成后,经过固定延迟时间开始下一次执行)watchConfigKeyValues方法。
@Timed("consul.watch-config-keys")
public void watchConfigKeyValues() {
if (this.running.get()) {
for (String context : this.consulIndexes.keySet()) {
// turn the context into a Consul folder path (unless our config format
// are FILES)
if (this.properties.getFormat() != FILES && !context.endsWith("/")) {
context = context + "/";
}
try {
Long currentIndex = this.consulIndexes.get(context);
if (currentIndex == null) {
currentIndex = -1L;
}
log.trace("watching consul for context '" + context + "' with index "
+ currentIndex);
// use the consul ACL token if found
String aclToken = this.properties.getAclToken();
if (StringUtils.isEmpty(aclToken)) {
aclToken = null;
}
// 调用consul的HTTP API,获取配置
Response<List<GetValue>> response = this.consul.getKVValues(context,
aclToken,
new QueryParams(this.properties.getWatch().getWaitTime(),
currentIndex));
// if response.value == null, response was a 404, otherwise it was a
// 200
// reducing churn if there wasn't anything
if (response.getValue() != null && !response.getValue().isEmpty()) {
Long newIndex = response.getConsulIndex();
// currentIndex是缓存了KV配置的上一次更新的版本号,newIndex是当前版本号,若两者不等,则说明配置已更新,所以需要刷新
if (newIndex != null && !newIndex.equals(currentIndex)) {
// don't publish the same index again, don't publish the first
// time (-1) so index can be primed
if (!this.consulIndexes.containsValue(newIndex)
&& !currentIndex.equals(-1L)) {
log.trace("Context " + context + " has new index "
+ newIndex);
RefreshEventData data = new RefreshEventData(context,
currentIndex, newIndex);
// 通过Spring的事件机制触发配置动态刷新
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 e) {
// only fail fast on the initial query, otherwise just log the error
if (this.firstTime && this.properties.isFailFast()) {
log.error(
"Fail fast is set and there was an error reading configuration from consul.");
ReflectionUtils.rethrowRuntimeException(e);
}
else if (log.isTraceEnabled()) {
log.trace("Error querying consul Key/Values for context '"
+ context + "'", e);
}
else if