Spring Cloud Config Server提供了微服务获取配置的功能,这些配置文件(application.yml或者application.properties)通常维护在git或者数据库中,而且支持通过RefreshScope动态刷新,使用起来还是比较灵活的。但是当微服务越来越多时,会遇到下面几个问题:

为了解决上述问题,我们可以从configServer服务着手进行改造,示意如下:
不同的服务ABC,不管是在配置中心仓库端配置了多少个文件,从ConfigServer返回的,一定是服务最终应用的配置。获取配置的方式,通常是调用ConfigServer的一个地址,如:
http://localhost:8021/common_rent/dev/aliyun_dev
common_rent是application name,dev是profile,aliyun_dev是label(git的分支)。这个地址的处理接口,是ConfigServer的EnvironmentController,所以通过拦截这个接口,将敏感信息或者公共配置抽取到configServer的application.yml, 返回前进行替换或者拼接,即可实现上述目的。
代码示例:
拦截器实现
@Component
@Aspect
public class ResourceLoaderInterceptor {
private static Log logger = LogFactory.getLog(ResourceLoaderInterceptor.class);
@Resource
ExternalProperties externalProperties;
@Around("execution(* org.springframework.cloud.config.server..*Controller.*(..)) ")
public Object commonPropertiesResolve(ProceedingJoinPoint joinPoint) throws Throwable {
    Object returnObj = null;
    Object[] args = joinPoint.getArgs();
    StopWatch stopWatch = new StopWatch();
    try {
        stopWatch.start();
        returnObj = joinPoint.proceed(args);
        if (Environment.class.isInstance(returnObj)) {
            Environment environment = (Environment) returnObj;
            if (environment.getPropertySources() != null && environment.getPropertySources().size() > 0) {
                for (PropertySource propertySource : environment.getPropertySources()) {
                    placeHolderResolve((Map) propertySource.getSource());
                }
            }
        }
    } catch (Throwable throwable) {
        logger.error(ExceptionUtils.getStackTrace(throwable));
    } finally {
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
    }
    return returnObj;
}
private void placeHolderResolve(Map source) {
    Map placeHolders = collectConfigSet();
    for (String key : source.keySet()) {
        Object value = source.get(key);
        Object valueAfterReplace = null;
        if (value != null) {
            if (String.class.isInstance(value) && ((String) value).contains("${ext.")) {
                String varExp = (String) value;
                for (String variable : placeHolders.keySet()) {
                    String vk = "${" + variable + "}";
                    if (varExp.contains(vk)) {
                        Object replaceValue = placeHolders.get(variable);
                        if (replaceValue != null) {
                            if (varExp.equalsIgnoreCase(vk)) {
                                valueAfterReplace = replaceValue;
                                break;
                            } else {
                                varExp = StringUtils.replace(varExp, vk, "" + replaceValue);
                                if (!varExp.contains("${")) {
                                    break;
                                }
                            }
                        } else {
                            logger.error("Property " + vk + " is not properly configured!");
                        }
                    }
                }
                if (valueAfterReplace != null) {
                    source.put(key, valueAfterReplace);
                } else if (varExp.contains("${")) {
                    logger.error("Property " + varExp + " is not properly configured!");
                } else {
                    source.put(key, varExp);
                }
            }
        }
    }
}
private Map collectConfigSet() {
    Map placeHolders = new HashMap<>();
    Field[] fields = ExternalProperties.class.getDeclaredFields();
    for (int i = 0; i < fields.length; i++) {
        try {
            Field propertiesField = fields[i];
            ResourcePrefix resourcePrefix = propertiesField.getAnnotation(ResourcePrefix.class);
            String prefix = resourcePrefix.value();
            ExtDataSource extDataSource = (ExtDataSource) BeanUtils.getPropertyDescriptor(ExternalProperties.class, propertiesField.getName()).getReadMethod().invoke(externalProperties);
            if (extDataSource != null) {
                Field[] fields2 = ExtDataSource.class.getDeclaredFields();
                for (Field datasourceField : fields2) {
                    try {
                        ResourcePrefix annotation = datasourceField.getAnnotation(ResourcePrefix.class);
                        String suffix = annotation.value();
                        Object sourceFieldValue = BeanUtils.getPropertyDescriptor(ExtDataSource.class, datasourceField.getName()).getReadMethod().invoke(extDataSource);
                        if (sourceFieldValue != null) {
                            placeHolders.put(prefix + "." + suffix, sourceFieldValue);
                        }
                    } catch (Exception e) {
                        logger.error(ExceptionUtils.getStackTrace(e));
                    }
                }
            }
        } catch (Exception e) {
            logger.error(ExceptionUtils.getStackTrace(e));
        }
    }
    return placeHolders;
}
}     @ConfigurationProperties(prefix = "external", ignoreUnknownFields = true)
public class ExternalProperties implements Serializable {
    @ResourcePrefix(value = "ext.spring.datasource")
    private ExtDataSource datasource;
    @ResourcePrefix(value = "ext.spring.data.mongodb")
    private ExtDataSource mongodb;
    @ResourcePrefix(value = "ext.spring.redis")
    private ExtDataSource redis;
    @ResourcePrefix(value = "ext.spring.rabbitmq")
    private ExtDataSource rabbitmq;
    public ExtDataSource getDatasource() {
        return datasource;
    }
    public void setDatasource(ExtDataSource datasource) {
        this.datasource = datasource;
    }
    public ExtDataSource getRabbitmq() {
        return rabbitmq;
    }
    public void setRabbitmq(ExtDataSource rabbitmq) {
        this.rabbitmq = rabbitmq;
    }
    public ExtDataSource getMongodb() {
        return mongodb;
    }
    public void setMongodb(ExtDataSource mongodb) {
        this.mongodb = mongodb;
    }
    public ExtDataSource getRedis() {
        return redis;
    }
    public void setRedis(ExtDataSource redis) {
        this.redis = redis;
    }
}ExtDataSource实现
public class ExtDataSource {
@ResourcePrefix(value = "host")
private String host;
@ResourcePrefix(value = "port")
private Integer port;
@ResourcePrefix(value = "url")
private String url;
@ResourcePrefix(value = "uri")
private String uri;
@ResourcePrefix(value = "username")
private String userName;
@ResourcePrefix(value = "password")
private String password;
public String getUrl() {
    return url;
}
public void setUrl(String url) {
    this.url = url;
}
public String getHost() {
    return host;
}
public void setHost(String host) {
    this.host = host;
}
public Integer getPort() {
    return port;
}
public void setPort(Integer port) {
    this.port = port;
}
public String getUri() {
    return uri;
}
public void setUri(String uri) {
    this.uri = uri;
}
public String getUserName() {
    return userName;
}
public void setUserName(String userName) {
    this.userName = userName;
}
public String getPassword() {
    return password;
}
public void setPassword(String password) {
    this.password = password;
}
}@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ResourcePrefix {
String value();
}然后在configServer的application.yml中增加相关信息,如
external:
  datasource:
    host: 122.122.111.111
    port: 3307
    userName: usr
    password: pwd
  mongodb:
    host: 122.122.111.111
    port: 20467
    uri: 122.122.111.111:20467,122.122.111.112:20467,122.122.111.112:20467
    userName: usr
    password:  pwd
  redis:
    uri: 122.122.111.113:6379,122.122.112.113:6379,122.122.111.113:6379
    password: redispassword
  rabbitmq:
    host: 122.122.111.113
    port: 20467
    userName: usr
    password: pwd将ServiceA的配置文件serviceA_dev.yml中的数据库相关信息替换成变量,以mysql为例
spring.datasource.uri: url: jdbc:mysql://#{ext.spring.datasource.host}:#{ext.spring.datasource.port}/dbName?useUnicode=true&characterEncoding=utf8,
serviceB和serviceC配置文件做同样处理,即可实现一次性替换。
后续如果需要增加公共配置,可以直接在ConfigServer的配置中间中增加,调整下拦截器的实现逻辑即可。
另外有需要云服务器可以了解下创新互联cdcxhl.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。