1、背景
Spring cloud config 以git为存储学习过程中,发现针对spring cloud config client 增加了@RefreshScope 后,执行curl –X POST http://ip:port/refresh成功后,对应client中的值没有改变。
-
项目配置
- Config Server
- POM 配置
|
- Application.yaml
server:
port: 8003
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/liuzdchn/config
username: [email protected]
password: lzd13973596
#默认为首次请求才会clone git 仓库 配置为启动时clone,可提前快速识别错误配置源
#clone-on-start: true
health:
repositories:
a-foo:
lable: dev
- ConfigServer
package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.context.config.annotation.RefreshScope;
@SpringBootApplication
@EnableConfigServer
public class ConfigServer {
public static void main(String[] args) {
SpringApplication.run(ConfigServer.class,args);
}
}
- Config Client 附解决方案
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
server:
port: 8005
spring:
application:
#对应config server 所获取配置文件的{application}
name: provider
cloud:
config:
uri: http://localhost:8003/
#对应{profile}
profile: foo
#对应{lable}
label: dev
package com.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ConfigClientController implements EnvironmentAware{
@Value("${profile}") //获取git仓库配置文件中 profile的值
private String profile;
@Autowired
private Environment environment;
@GetMapping("/profile")
public @ResponseBody String hello(){
// 解决refresh请求后,profile变量不更新问题
return environment.getProperty("profile");
}
@Override
public void setEnvironment(Environment environment) {
this.environment=environment;
}
}
2、问题
Refresh 后 configServer端请求配置文件已经更新
Client工程中profile 值仍为原始 defalut-2.0-change-0.9
3、问题排查
根据上述日志进入org.springframework.cloud.config.client.ConfigServicePropertySourceLocator#locate 整体逻辑
public PropertySource<?> locate(Environment environment) {
ConfigClientProperties properties = this.defaultProperties.override(environment);
CompositePropertySource composite = new CompositePropertySource("configService");
RestTemplate restTemplate = this.restTemplate == null ? this.getSecureRestTemplate(properties) : this.restTemplate;
Exception error = null;
String errorBody = null;
logger.info("Fetching config from server at: " + properties.getRawUri());
try {
String[] labels = new String[]{""};
if (StringUtils.hasText(properties.getLabel())) {
labels = StringUtils.commaDelimitedListToStringArray(properties.getLabel());
}
String state = ConfigClientStateHolder.getState();
String[] var9 = labels;
int var10 = labels.length;
for(int var11 = 0; var11 < var10; ++var11) {
String label = var9[var11];
org.springframework.cloud.config.environment.Environment result = this.getRemoteEnvironment(restTemplate, properties, label.trim(), state);
if (result != null) {
logger.info(String.format("Located environment: name=%s, profiles=%s, label=%s, version=%s, state=%s", result.getName(), result.getProfiles() == null ? "" : Arrays.asList(result.getProfiles()), result.getLabel(), result.getVersion(), result.getState()));
if (result.getPropertySources() != null) {
Iterator var14 = result.getPropertySources().iterator();
while(var14.hasNext()) {
org.springframework.cloud.config.environment.PropertySource source = (org.springframework.cloud.config.environment.PropertySource)var14.next();
Map<String, Object> map = source.getSource();
composite.addPropertySource(new MapPropertySource(source.getName(), map));
}
}
if (StringUtils.hasText(result.getState()) || StringUtils.hasText(result.getVersion())) {
HashMap<String, Object> map = new HashMap();
this.putValue(map, "config.client.state", result.getState());
this.putValue(map, "config.client.version", result.getVersion());
composite.addFirstPropertySource(new MapPropertySource("configClient", map));
}
return composite;
}
}
} catch (HttpServerErrorException var17) {
error = var17;
if (MediaType.APPLICATION_JSON.includes(var17.getResponseHeaders().getContentType())) {
errorBody = var17.getResponseBodyAsString();
}
} catch (Exception var18) {
error = var18;
}
if (properties.isFailFast()) {
throw new IllegalStateException("Could not locate PropertySource and the fail fast property is set, failing", (Throwable)error);
} else {
logger.warn("Could not locate PropertySource: " + (errorBody == null ? (error == null ? "label not found" : ((Exception)error).getMessage()) : errorBody));
return null;
}
}
根据背景色的执行过程,主要就是访问http://localhost:8003/provider/foo/dev 利用config server刷新属性文件,并通过debug情况观察数据已经更新到environment变量中,说明数据已经刷新到client中。
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration#initialize 这里就是做environment内容bootstrapProperties关联内容更新
public void initialize(ConfigurableApplicationContext applicationContext) {
CompositePropertySource composite = new CompositePropertySource("bootstrapProperties");
AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
boolean empty = true;
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Iterator var5 = this.propertySourceLocators.iterator();
while(var5.hasNext()) {
PropertySourceLocator locator = (PropertySourceLocator)var5.next();
PropertySource<?> source = null;
source = locator.locate(environment);
if (source != null) {
logger.info("Located property source: " + source);
composite.addPropertySource(source);
empty = false;
}
}
if (!empty) {
MutablePropertySources propertySources = environment.getPropertySources();
String logConfig = environment.resolvePlaceholders("${logging.config:}");
LogFile logFile = LogFile.get(environment);
if (propertySources.contains("bootstrapProperties")) {
propertySources.remove("bootstrapProperties");
}
this.insertPropertySources(propertySources, composite);
this.reinitializeLoggingSystem(environment, logConfig, logFile);
this.setLogLevels(environment);
}
}
当我们再次访问http://localhost:8005/profile 查看client的profile属性时,发现其仍然是旧属性,这里我理解为当前controller对象的值在client程序启动第一次访问从config server获取到environment环境变量后,已经对变量赋值,后续refresh仅针对environment环境变量更新,并没有更新对象中的profile变量只,故一直显示“没有刷新”。
4、问题解决
当我们排查到变量在refresh后会更新environment变量后,我们可以通过environment.getProperty("profile");解决
@Controller public class ConfigClientController implements EnvironmentAware{ @Value("${profile}") //获取git仓库配置文件中 profile的值 private String profile; @Autowired private Environment environment; @GetMapping("/profile") public @ResponseBody String hello(){ // 解决refresh请求后,profile变量不更新问题 return environment.getProperty("profile"); } @Override public void setEnvironment(Environment environment) { this.environment=environment; } }