刷新时设置的 profiles 只是 应用容器之外其他容器的 active profiles
如果要实现动态设置 profiles 的功能,应该放到应用层做:
# 由于 @RefreshScope 的 destroy 生命周期函数在 Bean 每次刷新时都会调用一次,因此可以利用这个机制发布
来做刷新
@PreDestroy
public void des(){
log.warn("---毁灭者---");
log.info("current spring.profiles.active {}",env);
context.publishEvent(new ActiveProfileEvent(this, (ConfigurableEnvironment) environment));
}
@EventListener(ActiveProfileEvent.class)
public void active(ActiveProfileEvent event){
log.info("try to active");
mergeThenActiveIfPossible(event.getEnvironment());
}
private String[] mergeThenActiveIfPossible(ConfigurableEnvironment environment) {
String[] currentProfiles = environment.getActiveProfiles();
Set<String> latestProfiles = StringUtils.commaDelimitedListToSet((String) LaixPropertySourceRepository.getLatest("spring.profiles.active"));
for (String profile : currentProfiles) {
if (!latestProfiles.contains(profile)) {
return currentProfiles;
}
}
String[] refreshedProfiles = latestProfiles.toArray(new String[0]);
environment.setActiveProfiles(refreshedProfiles);
return refreshedProfiles;
}
public class ActiveProfileEvent extends ApplicationEvent {
private ConfigurableEnvironment environment;
public ActiveProfileEvent(Object source, ConfigurableEnvironment environment) {
super(source);
this.environment = environment;
}
public ConfigurableEnvironment getEnvironment() {
return environment;
}
}
另一个,如果通过 远程 active profiles 来远程设置 profile,可以实现额外配置拉取,但是也需要重新 watch,同时会使 laix-meta 很蛋疼(前一步骤已经有了)
会导致 Repository 会 laix-config 显示不一致,唯一的解法是 addLast,但是 addLast 还灰度个毛。。。也破坏了整个优先级的约定
后续需要把 LaixPropertySourceRepository 改成队列可能比较好解决这个问题
同时还要添加动态删除的逻辑
loadExtProfilesIfPossible(profiles, loader, composite);
private boolean loadExtProfilesIfPossible(String[] bootProfiles, PropertyLoader loader, CompositePropertySource composite){
Object exts = LaixPropertySourceRepository.getLatest("spring.profiles.active");
if(exts instanceof String){
Set<String> remoteProfiles = StringUtils.commaDelimitedListToSet((String)exts);
if(!remoteProfiles.containsAll(Arrays.asList(bootProfiles))){
log.warn("remote profiles {} doesn't contain all the boot profiles {}, none extra profiles will loading", exts, bootProfiles);
}
remoteProfiles.removeAll(Arrays.asList(bootProfiles));
log.info("loading ext profiles {}", remoteProfiles);
Set<String> extBases = new LinkedHashSet<>();
for(String profile: remoteProfiles){
String profilePrefix = laixConfigProperties.appKeyPrefix(profile);
LaixPropertySource applicationSource = loader.load(profilePrefix, true, LaixConfigProperties.DEFAULT_VERSION);
composite.addFirstPropertySource(applicationSource);
ClientKeyValue mayBases = ClientKVHolder.get(laixConfigProperties.appBasesKey(profile));
if(mayBases!=null){
extBases.addAll(StringUtils.commaDelimitedListToSet(mayBases.getValue()));
}
}
Set<String> baseGroups = new LinkedHashSet<>();
extBases.stream().map(baseName-> laixConfigProperties.baseKeyPrefix(baseName)).forEachOrdered(baseGroups::add);
log.info("loading ext base configurations {}",baseGroups.stream().map(ServerKeyValueUtils::decodeBaseName).collect(Collectors.toList()));
Set<String> immutableGroups = extBases.stream()
.filter(b->laixConfigProperties.getImmutableBases().contains(b))
.map(baseName-> laixConfigProperties.baseKeyPrefix(baseName)).collect(Collectors.toSet());
baseGroups.removeAll(immutableGroups);
baseGroups.parallelStream().map(bg->loader.load(bg,true, LaixConfigProperties.DEFAULT_VERSION)).forEachOrdered(composite::addPropertySource);
immutableGroups.parallelStream().map(bg->loader.load(bg,false, LaixConfigProperties.DEFAULT_VERSION)).forEachOrdered(composite::addPropertySource);
String preBases = StringUtils.collectionToCommaDelimitedString(laixConfigProperties.getBases());
String newBases = preBases.concat(",").concat(StringUtils.collectionToCommaDelimitedString(extBases));
laixConfigProperties.setBases(newBases);
// 解决缺省情况下,环境中没有 laix.config.bases 配置项,无法动态绑定到 laix-properties 中的问题
composite.addPropertySource(new MapPropertySource(
"laix-meta",Collections.singletonMap(LaixConfigProperties.PREFIX+".bases"
,newBases)));
return true;
}
return false;
}
watch 解决办法,在 LaixContextRefresher 中添加一个 EventListener 即可
@EventListener(EnvironmentChangeEvent.class)
public void changed(EnvironmentChangeEvent event) {
if(event.getKeys().contains("spring.profiles.active")){
startWatch();
}
}