分布式配置
Spring Cloud分布式配置服务由服务器端和客户端共同组成,Server端提供配置信息的存储管理,客户端完成配置信息的调度,工程结构如下
套用一个图来解释该部分在springCloud分布式系统中的结构
上面的图片说明了四个微服务以及各个服务之间的依赖关系。
configuration service 处于最顶端,黄色标识,而且被其他微服务所依赖。
discovery service 处于最低端,蓝色标识,同时也被其他服务所依赖。
- bootstrap.yml
- application.yml
- 【注】建议采用bootstrap+application组合形式,一般bootstrap配置工程不可变参数,且在启动时需要设定的内容,application配置可变参数
J360-cloud-configserver
1、POM依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
2、注解
-
configserver提供了HTTP,为外部资源提供API配置的服务,只需要在Springboot程序中提供@EnableConfigServer注解即可。
- @EnableConfigServer
- 增加以下依赖 eureka(可选,作为discover服务器必选)
3、配置文件
bootstrap.properties
spring.application.name=configserver
spring.cloud.config.uri=http://localhost:8888
application.properties
server.port=8888
spring.cloud.config.server.git.uri=https://github.com/xuminwlt/config-repo
4、spring-boot:run
http://localhost:8888/configserver/application.properties
1、POM
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
2、配置
bootstrap.properties
spring.application.name=client
spring.cloud.config.uri=http://localhost:8888
application.properties
server.port=8080
apply.message=local message
user.username=xumin
3、spring-boot:run
4、http://localhost:8080/env 查看环境变量
5、http://localhost:8080/refresh post方法,更新client中的配置
【注】环境变量刷新
- post -> localhost:8080/refresh
- 定时
- 手动
环境变量仓库
默认的环境变量库使用git服务,也可以使用文件系统的服务
1、git服务
https://github.com/xuminwlt/config-repo
2、文件服务
使用 spring.profiles.active=native
环境变量仓库使用3个变量:
- {application} 在客户端映射到"spring.application.name"
- {profile} 在客户端映射到"spring.active.profiles",使用逗号分隔
- {label}这是一个服务器端功能标签“版本”的一组配置文件
自动刷新
当环境变量参数通过git更新后,客户端自动更新:
1:可以使用 post /refresh来执行,客户端调用更新功能,configServer pull git完成后,更新环境变量信息,客户端获取最新的环境变量,并且更新
2:定时器执行:每隔X时间段更新一次
3:根据需要执行:只需要将定时器调用的更新接口暴露成服务形式调用
多客户端更新服务
1:通过Spring Cloud Bus更新
2:其他广播形式调用接口
调用接口:
/**
* 手动刷新git更新方案,每20S执行一次,可以修改成手动执行,同/refresh
* @link RefreshEndpoint
* */
public void refresh() {
Map<String, Object> before = extract(context.getEnvironment()
.getPropertySources());
addConfigFilesToEnvironment();
Set<String> keys = changes(before,
extract(context.getEnvironment().getPropertySources())).keySet();
scope.refreshAll();
context.publishEvent(new EnvironmentChangeEvent(keys));
}
@Configuration
protected static class Empty {
}
private void addConfigFilesToEnvironment() {
ConfigurableApplicationContext capture = null;
try {
capture = new SpringApplicationBuilder(Empty.class).showBanner(false)
.web(false).environment(context.getEnvironment()).run();
MutablePropertySources target = context.getEnvironment().getPropertySources();
for (PropertySource<?> source : capture.getEnvironment().getPropertySources()) {
String name = source.getName();
if (!standardSources.contains(name)) {
if (target.contains(name)) {
target.replace(name, source);
}
else {
if (target.contains("defaultProperties")) {
target.addBefore("defaultProperties", source);
}
else {
target.addLast(source);
}
}
}
}
}
finally {
while (capture != null) {
capture.close();
ApplicationContext parent = capture.getParent();
if (parent instanceof ConfigurableApplicationContext) {
capture = (ConfigurableApplicationContext) parent;
} else {
capture = null;
}
}
}
}
private Map<String, Object> changes(Map<String, Object> before,
Map<String, Object> after) {
Map<String, Object> result = new HashMap<String, Object>();
for (String key : before.keySet()) {
if (!after.containsKey(key)) {
result.put(key, null);
}
else if (!equal(before.get(key), after.get(key))) {
result.put(key, after.get(key));
}
}
for (String key : after.keySet()) {
if (!before.containsKey(key)) {
result.put(key, after.get(key));
}
}
return result;
}
private boolean equal(Object one, Object two) {
if (one == null && two == null) {
return true;
}
if (one == null || two == null) {
return false;
}
return one.equals(two);
}
private Map<String, Object> extract(MutablePropertySources propertySources) {
Map<String, Object> result = new HashMap<String, Object>();
for (PropertySource<?> parent : propertySources) {
if (!standardSources.contains(parent.getName())) {
extract(parent, result);
}
}
return result;
}
private void extract(PropertySource<?> parent, Map<String, Object> result) {
if (parent instanceof CompositePropertySource) {
try {
for (PropertySource<?> source : ((CompositePropertySource) parent)
.getPropertySources()) {
extract(source, result);
}
}
catch (Exception e) {
return;
}
}
else if (parent instanceof EnumerablePropertySource) {
for (String key : ((EnumerablePropertySource<?>) parent).getPropertyNames()) {
result.put(key, parent.getProperty(key));
}
}
}
文章转载自 开源中国社区[https://www.oschina.net]