问题描述
使用@RefreshScope会刷新在Sprign IOC中所有Bean中使用@Value的值,但是在配置类中使用方法去配置的其他类参数并不会改变,例如
@Slf4j
@Configuration
@RefreshScope
public class SentinelConfig {
@Bean
public ZuulFilter sentinelZuulPreFilter() {
return new SentinelZuulPreFilter();
}
@Bean
public ZuulFilter sentinelZuulPostFilter() {
return new SentinelZuulPostFilter();
}
@Bean
public ZuulFilter sentinelZuulErrorFilter() {
return new SentinelZuulErrorFilter();
}
@PostConstruct
public void init() {
ZuulBlockFallbackManager.registerProvider(new MyBlockFallbackProvider());
Config config = ConfigService.getAppConfig();
//Nacos后台配置更新,flowRules并不会改变,需要手动loadRules
String flowRulesJson = config.getProperty("sentinel.flowRules",
"[{\"burst\":0,\"controlBehavior\":0,\"count\":3.0,\"grade\":1,\"intervalSec\":1,\"maxQueueingTimeoutMs\":500,\"resource\":\"appblog-service\",\"resourceMode\":1}]");
Set<GatewayFlowRule> flowRules = JSON.parseObject(flowRulesJson, new TypeReference<Set<GatewayFlowRule>>(){});
GatewayRuleManager.loadRules(flowRules);
}
}
解决方案
//使用此方法监听事件
@EventListener
public void envListener(EnvironmentChangeEvent event) {
}
@Slf4j
@Configuration
@RefreshScope
public class SentinelConfig {
@Value("${sentinel.flowRules:[{\"burst\":0,\"controlBehavior\":0,\"count\":3.0,\"grade\":1,\"intervalSec\":1,\"maxQueueingTimeoutMs\":500,\"resource\":\"appblog-service\",\"resourceMode\":1}]}")
private String flowRulesJson;
@Bean
public ZuulFilter sentinelZuulPreFilter() {
return new SentinelZuulPreFilter();
}
@Bean
public ZuulFilter sentinelZuulPostFilter() {
return new SentinelZuulPostFilter();
}
@Bean
public ZuulFilter sentinelZuulErrorFilter() {
return new SentinelZuulErrorFilter();
}
@PostConstruct
public void init() {
ZuulBlockFallbackManager.registerProvider(new MyBlockFallbackProvider());
Config config = ConfigService.getAppConfig();
String flowRulesJson = config.getProperty("sentinel.flowRules",
"[{\"burst\":0,\"controlBehavior\":0,\"count\":3.0,\"grade\":1,\"intervalSec\":1,\"maxQueueingTimeoutMs\":500,\"resource\":\"appblog-service\",\"resourceMode\":1}]");
Set<GatewayFlowRule> flowRules = JSON.parseObject(flowRulesJson, new TypeReference<Set<GatewayFlowRule>>(){});
GatewayRuleManager.loadRules(flowRules);
}
@EventListener
public void envChangeListener(EnvironmentChangeEvent event) {
log.info("Sentinel配置刷新");
log.info(flowRulesJson);
Set<GatewayFlowRule> flowRules = JSON.parseObject(flowRulesJson, new TypeReference<Set<GatewayFlowRule>>(){});
GatewayRuleManager.loadRules(flowRules);
}
}
原因分析
在调用刷新方法是会产生一个EnvironmentChangeEvent事件。进入ContextRefresher源码,看下refresh接口;
public synchronized Set<String> refresh() {
Map<String, Object> before = extract(
this.context.getEnvironment().getPropertySources());
addConfigFilesToEnvironment();
Set<String> keys = changes(before,
extract(this.context.getEnvironment().getPropertySources())).keySet();
// 注意这一行,抛出了一个变更事件
this.context.publishEvent(new EnvironmentChangeEvent(context, keys));
this.scope.refreshAll();
return keys;
}