Spring Cloud Config Server 提供了微服务获取配置的功能, 这些配置文件 (application.YAML 或者 application.properties) 通常维护在 Git 或者数据库中, 而且支持通过 RefreshScope 动态刷新, 使用起来还是比较灵活的. 但是当微服务越来越多时, 会遇到下面几个问题:
配置文件的敏感数如数据库地址和账号信息, 据呈现在每个配置文件中, 替换起来需要一个个配置文件进行修改.
各个微服务配置文件存在很多冗余配置(如 Eureka,Feign), 一旦这些部分调整, 需要针对每个微服务进行调整, 运维压力大增.
为了解决上述问题, 我们可以从 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.YAML, 返回前进行替换或者拼接, 即可实现上述目的.
代码示例:
拦截器实现
- @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<String, String>) propertySource.getSource());
- }
- }
- }
- } catch (Throwable throwable) {
- logger.error(ExceptionUtils.getStackTrace(throwable));
- } finally {
- stopWatch.stop();
- System.out.println(stopWatch.getTotalTimeMillis());
- }
- return returnObj;
- }
- private void placeHolderResolve(Map<String, String> source) {
- Map<String, String> placeHolders = collectConfigSet();
- for (String key : source.keySet()) {
- String value = source.get(key);
- if (value != null && value.contains("#{")) {
- for (String variable : placeHolders.keySet()) {
- String vk = "#{" + variable + "}";
- value = StringUtils.replace(value, vk, placeHolders.get(variable));
- }
- source.put(key, value);
- }
- }
- }
- private Map<String, String> collectConfigSet() {
- Map<String, String> 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();
- String sourceFieldValue = (String) BeanUtils.getPropertyDescriptor(ExtDataSource.class, datasourceField.getName()).getReadMethod().invoke(extDataSource);
- if (StringUtils.isNotEmpty(sourceFieldValue)) {
- placeHolders.put(prefix + "." + suffix, sourceFieldValue);
- }
- } catch (Exception e) {
- logger.error(ExceptionUtils.getStackTrace(e));
- }
- }
- }
- } catch (Exception e) {
- logger.error(ExceptionUtils.getStackTrace(e));
- }
- }
- return placeHolders;
- }
- }
ExternalProperites 实现
- @ConfigurationProperties(prefix = "external", ignoreUnknownFields = true)
- public class ExternalProperties implements Serializable {
- @ResourcePrefix(value = "spring.datasource")
- private ExtDataSource datasource;
- @ResourcePrefix(value = "spring.data.mongodb")
- private ExtDataSource MongoDB;
- @ResourcePrefix(value = "spring.data.redis")
- private ExtDataSource Redis;
- @ResourcePrefix(value = "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 String 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 String getPort() {
- return port;
- }
- public void setPort(String 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;
- }
- }
ResourcePrefix 实现
- @Target({
- ElementType.FIELD, ElementType.TYPE
- })
- @Retention(RetentionPolicy.RUNTIME)
- public @interface ResourcePrefix {
- String value();
- }
然后在 configServer 的 application.YAML 中增加相关信息, 如
- 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.YAML 中的数据库相关信息替换成变量, 以 MySQL 为例
spring.datasource.uri: url: jdbc:MySQL://#{spring.datasource.host}:#{spring.datasource.port}/dbName?useUnicode=true&characterEncoding=utf8,
serviceB 和 serviceC 配置文件做同样处理, 即可实现一次性替换.
后续如果需要增加公共配置, 可以直接在 ConfigServer 的配置中间中增加, 调整下拦截器的实现逻辑即可.
来源: http://www.bubuko.com/infodetail-3164613.html