您好,登錄后才能下訂單哦!
Spring Cloud Config Server提供了微服務獲取配置的功能,這些配置文件(application.yml或者application.properties)通常維護在git或者數(shù)據(jù)庫中,而且支持通過RefreshScope動態(tài)刷新,使用起來還是比較靈活的。但是當微服務越來越多時,會遇到下面幾個問題:
為了解決上述問題,我們可以從configServer服務著手進行改造,示意如下:
不同的服務ABC,不管是在配置中心倉庫端配置了多少個文件,從ConfigServer返回的,一定是服務最終應用的配置。獲取配置的方式,通常是調(diào)用ConfigServer的一個地址,如:
http://localhost:8021/common_rent/dev/aliyun_dev
common_rent是application name,dev是profile,aliyun_dev是label(git的分支)。這個地址的處理接口,是ConfigServer的EnvironmentController,所以通過攔截這個接口,將敏感信息或者公共配置抽取到configServer的application.yml, 返回前進行替換或者拼接,即可實現(xiàn)上述目的。
代碼示例:
攔截器實現(xiàn)
@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, Object>) 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, Object> source) {
Map<String, Object> 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<String, Object> collectConfigSet() {
Map<String, Object> 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實現(xiàn)
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中增加相關(guān)信息,如
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中的數(shù)據(jù)庫相關(guān)信息替換成變量,以mysql為例
spring.datasource.uri: url: jdbc:mysql://#{ext.spring.datasource.host}:#{ext.spring.datasource.port}/dbName?useUnicode=true&characterEncoding=utf8,
serviceB和serviceC配置文件做同樣處理,即可實現(xiàn)一次性替換。
后續(xù)如果需要增加公共配置,可以直接在ConfigServer的配置中間中增加,調(diào)整下攔截器的實現(xiàn)邏輯即可。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。