Spring Cloud 微服务公共配置处理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Cloud 微服务公共配置处理相关的知识,希望对你有一定的参考价值。
Spring Cloud Config Server提供了微服务获取配置的功能,这些配置文件(application.yml或者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.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<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.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://#spring.datasource.host:#spring.datasource.port/dbName?useUnicode=true&characterEncoding=utf8,
serviceB和serviceC配置文件做同样处理,即可实现一次性替换。
后续如果需要增加公共配置,可以直接在ConfigServer的配置中间中增加,调整下拦截器的实现逻辑即可。
以上是关于Spring Cloud 微服务公共配置处理的主要内容,如果未能解决你的问题,请参考以下文章
Spring Cloud Alibaba - 18 Nacos Config配置中心加载相同微服务的不同环境下的通用配置