服务治理consul之配置中心
·
作为配置中心是通过核心类ConsulConfigAutoConfiguration、ConsulConfigBootstrapConfiguration实现的。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul-config</artifactId>
</dependency>
1.ConsulConfigAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnConsulEnabled
@ConditionalOnProperty(name = "spring.cloud.consul.config.enabled", matchIfMissing = true)
public class ConsulConfigAutoConfiguration {
/**
* Name of the config watch task scheduler bean.
*/
public static final String CONFIG_WATCH_TASK_SCHEDULER_NAME = "configWatchTaskScheduler";
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RefreshEndpoint.class)
protected static class ConsulRefreshConfiguration {
@Bean
@ConditionalOnProperty(name = "spring.cloud.consul.config.watch.enabled",
matchIfMissing = true)
public ConfigWatch configWatch(ConsulConfigProperties properties,
ConsulPropertySourceLocator locator, ConsulClient consul,
@Qualifier(CONFIG_WATCH_TASK_SCHEDULER_NAME) TaskScheduler taskScheduler) {
return new ConfigWatch(properties, consul, locator.getContextIndexes(),
taskScheduler);
}
@Bean(name = CONFIG_WATCH_TASK_SCHEDULER_NAME)
@ConditionalOnProperty(name = "spring.cloud.consul.config.watch.enabled",
matchIfMissing = true)
public TaskScheduler configWatchTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
}
}
2.ConsulConfigBootstrapConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnConsulEnabled
public class ConsulConfigBootstrapConfiguration {
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@Import(ConsulAutoConfiguration.class)
@ConditionalOnProperty(name = "spring.cloud.consul.config.enabled",matchIfMissing = true)
protected static class ConsulPropertySourceConfiguration {
@Autowired
private ConsulClient consul;
@Bean
@ConditionalOnMissingBean
public ConsulConfigProperties consulConfigProperties() {
return new ConsulConfigProperties();
}
@Bean
public ConsulPropertySourceLocator consulPropertySourceLocator(
ConsulConfigProperties consulConfigProperties) {
return new ConsulPropertySourceLocator(this.consul, consulConfigProperties);
}
}
}
3.ConsulPropertySourceLocator
public class ConsulPropertySourceLocator implements PropertySourceLocator {
private final List<String> contexts = new ArrayList<>();
public PropertySource<?> locate(Environment environment) {
if (environment instanceof ConfigurableEnvironment) {
ConfigurableEnvironment env = (ConfigurableEnvironment) environment;
String appName = this.properties.getName();
if (appName == null) {//name属性 如果没有显式配置,则选择spring.application.name
appName = env.getProperty("spring.application.name");
}
List<String> profiles = Arrays.asList(env.getActiveProfiles());
String prefix = this.properties.getPrefix();//默认值为 config
List<String> suffixes = new ArrayList<>();
if (this.properties.getFormat() != FILES) {
suffixes.add("/");
}else {
suffixes.add(".yml");
suffixes.add(".yaml");
suffixes.add(".properties");
}
//defaultContext:config/application
String defaultContext = getContext(prefix,this.properties.getDefaultContext());
for (String suffix : suffixes) {
this.contexts.add(defaultContext + suffix);// config/application/
}
for (String suffix : suffixes) {// config/application,dev/
addProfiles(this.contexts, defaultContext, profiles, suffix);
}
String baseContext = getContext(prefix, appName);// config/appName
for (String suffix : suffixes) {
this.contexts.add(baseContext + suffix);// config/appName/
}
for (String suffix : suffixes) {
addProfiles(this.contexts, baseContext, profiles, suffix);// config/appName,dev/
}
Collections.reverse(this.contexts);
CompositePropertySource composite = new CompositePropertySource("consul");
for (String propertySourceContext : this.contexts) {
try {// config/application、config/application,profiles/、config/appName、config/appName,profiles/
ConsulPropertySource propertySource = null;
if (this.properties.getFormat() == FILES) {
Response<GetValue> response = this.consul.getKVValue(
propertySourceContext, this.properties.getAclToken());
addIndex(propertySourceContext, response.getConsulIndex());
if (response.getValue() != null) {
ConsulFilesPropertySource filesPropertySource = new ConsulFilesPropertySource(
propertySourceContext, this.consul, this.properties);
filesPropertySource.init(response.getValue());
propertySource = filesPropertySource;
}
}
else {
propertySource = create(propertySourceContext, this.contextIndex);
}
if (propertySource != null) {
composite.addPropertySource(propertySource);
}
}
}
return composite;
}
return null;
}
private ConsulPropertySource create(String context, Map<String, Long> contextIndex) {
ConsulPropertySource propertySource = new ConsulPropertySource(context,
this.consul, this.properties);
propertySource.init();
addIndex(context, propertySource.getInitialIndex());
return propertySource;
}
}
public class ConsulPropertySource extends EnumerablePropertySource<ConsulClient> {
private final Map<String, Object> properties = new LinkedHashMap<>();
private String context;
private ConsulConfigProperties configProperties;
private Long initialIndex;
public void init() {
if (!this.context.endsWith("/")) {
this.context = this.context + "/";
}
// 如果kv方式,通过http方式请求接口 /v1/kv/config/consul-producer,dev/
Response<List<GetValue>> response = this.source.getKVValues(this.context,
this.configProperties.getAclToken(), QueryParams.DEFAULT);
this.initialIndex = response.getConsulIndex();
final List<GetValue> values = response.getValue();
ConsulConfigProperties.Format format = this.configProperties.getFormat();
switch (format) {
case KEY_VALUE:
parsePropertiesInKeyValueFormat(values);
break;
case PROPERTIES:
case YAML:
parsePropertiesWithNonKeyValueFormat(values, format);
}
}
protected void parsePropertiesInKeyValueFormat(List<GetValue> values) {
if (values == null) {
return;
}
for (GetValue getValue : values) {
String key = getValue.getKey();// key = config/appName,dev/key
if (!StringUtils.endsWithIgnoreCase(key, "/")) {// 如果key的后缀没有"/"表明是配置的kv,并非是目录
key = key.replace(this.context, "").replace('/', '.');// 截取得到最终应用中的key
String value = getValue.getDecodedValue();
this.properties.put(key, value);
}
}
}
protected void parsePropertiesWithNonKeyValueFormat(List<GetValue> values,ConsulConfigProperties.Format format) {
if (values == null) {
return;
}
for (GetValue getValue : values) {
// 从key中截取config/appName,dev/data,最终得到key = data
String key = getValue.getKey().replace(this.context, "");
//判断key = data 是否等于 ConsulConfigProperties中dataKey值默认为data,注意data不能是目录,即存在后缀"/"
if (this.configProperties.getDataKey().equals(key)) {
parseValue(getValue, format);
}
}
}
protected void parseValue(GetValue getValue, ConsulConfigProperties.Format format) {
String value = getValue.getDecodedValue();//获取到yaml配置的所有kv
if (value == null) {
return;
}
Properties props = generateProperties(value, format);
for (Map.Entry entry : props.entrySet()) {
this.properties.put(entry.getKey().toString(), entry.getValue());
}
}
//因为value是配置中心全部的配置信息,并且是字符串类型,所以需要解析字符串
protected Properties generateProperties(String value,ConsulConfigProperties.Format format) {
final Properties props = new Properties();
if (format == PROPERTIES) {// 如果项目中配置的是PROPERTIES,即配置中心配置方式是k = v,则通过"="进行解析
props.load(new ByteArrayInputStream(value.getBytes("ISO-8859-1")));
return props;
}
else if (format == YAML) {//如果项目中配置的是YAML,即配置中心配置方式是k : v,则通过":"进行解析
final YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ByteArrayResource(value.getBytes(Charset.forName("UTF-8"))));
return yaml.getObject();
}
return props;
}
}
配置中心存在四种方式的配置形式即JSON、yaml、HCL & xml。
默认即spring.cloud.consul.config.format=KEY_VALUE,如下所示:

如果kv方式则配置中心的配置方式没有效果,kv配置方式如上图所示。
spring.cloud.consul.config.format=yaml或者PROPERTIES,且配置中心如下所示:

如果yaml || PROPERTIES 方式则配置中心的配置方式选择对应的yaml,yaml配置方式如图所示。
4.ConfigWatch
存在一个定时任务,感知配置文件信息的变化。如果存在配置信息变更则触发RefreshEvent类型的事件,并且由RefreshEventListener监听器处理该事件。核心逻辑删除scopedTarget.xxx对应的bean实例缓存信息
更多推荐
所有评论(0)