这是我参与更文挑战的第 15 天,活动详情查看: 更文挑战
《配置中心 Spring Cloud Config 详解》系列文章更新,一起在技术的路上精进!本系列文章将会介绍Spring Cloud 中提供了分布式配置中心Spring Cloud Config。应用服务中除了实现系统功能的代码,还需要连接资源和其它应用,经常有很多需要在外部配置的数据去调整应用的行为,如切换不同的数据库,设置功能开关等。随着微服务的不断增加,需要系统具备可伸缩和可扩展性,除此之外就是管理相当多的服务实例的配置数据。在应用的开发阶段由各个服务自治,但是到了生产环境之后会给运维带来很大的麻烦,特别是微服务的规模比较大,配置的更新更为麻烦。为此,系统需要建立一个统一的配置管理中心。
在前面的文章,我们主要介绍了 Spring Cloud Config 客户端如何通过 HTTP URI 和服务发下的方式来指定配置服务器,以及相关的实现细节。本文将会介绍客户端实现中对于服务端配置信息变更监听 ConfigServerInstanceProvider 和 EventListener 的相关实现。
ConfigServerInstanceProvider
从前面文章的ConfigServerInstanceProvider
构造方式看出,实例化ConfigServerInstanceProvider
需要服务发现的客户端DiscoveryClient
。其提供的主要方法getConfigServerInstance
,通过传入的serviceId参数,获取对应的服务实例。
public class ConfigServerInstanceProvider {
public ConfigServerInstanceProvider(DiscoveryClient client) {
this.client = client;
}
@Retryable(interceptor = "configServerRetryInterceptor")
public ServiceInstance getConfigServerInstance(String serviceId) {
logger.debug("Locating configserver (" + serviceId + ") via discovery");
List<ServiceInstance> instances = this.client.getInstances(serviceId);
if (instances.isEmpty()) {
throw new IllegalStateException(
"No instances found of configserver (" + serviceId + ")");
}
ServiceInstance instance = instances.get(0);
return instance;
}
}
复制代码
上述代码实现逻辑很清晰,主要依赖于前面的 DiscoveryClientConfigServiceBootstrapConfiguration
注入的对象DiscoveryClient
,通过client获取对应serviceId的实例。
EventListener
下面我们看一下上面配置的两个事件监听器:环境上下文刷新和心跳事件。
环境上下文刷新事件,ContextRefreshedEvent
的父类继承自抽象类ApplicationEvent
,当ApplicationContext
被初始化或者刷新时会唤起该事件。
public class ContextRefreshedEvent extends ApplicationContextEvent {
// 创建了一个新的上下文刷新事件,参数是初始化了的ApplicationContext
public ContextRefreshedEvent(ApplicationContext source) {
super(source);
}
}
复制代码
心跳事件定义在discovery client中,如果支持来自discovery server心跳,DiscoveryClient的实现能够广播。提供给监听器一个基本的服务目录状态变更的指示。
public class HeartbeatEvent extends ApplicationEvent {
private final Object state;
public HeartbeatEvent(Object source, Object state) { // 1
super(source);
this.state = state;
}
//
public Object getValue() { // 2
return this.state;
}
}
复制代码
- 创建一个新的事件,参数通常为discovery client和状态值,连个参数都不依赖于具体的内容和格式
- 代表服务目录的状态值。唯一的需求当目录更新了,该状态值也需要更新,如同一个版本计数器一样简单。
介绍完这两个事件,我们发现他们的监听器都依赖于refresh()
方法,下面我们具体看下其实现的功能。
private void refresh() {
try {
String serviceId = this.config.getDiscovery().getServiceId(); // 1
ServiceInstance server = this.instanceProvider
.getConfigServerInstance(serviceId);
String url = getHomePage(server);
if (server.getMetadata().containsKey("password")) { // 2
String user = server.getMetadata().get("user");
user = user == null ? "user" : user;
this.config.setUsername(user); // 3
String password = server.getMetadata().get("password");
this.config.setPassword(password);
}
if (server.getMetadata().containsKey("configPath")) {
String path = server.getMetadata().get("configPath"); // 4
if (url.endsWith("/") && path.startsWith("/")) {
url = url.substring(0, url.length() - 1);
}
url = url + path;
}
this.config.setUri(url);
}
}
复制代码
- 获取serviceId的服务实例
- 刷新获取到的服务实例的元数据信息
- 更新用户名、密码
- 更新configPath
refresh()
方法其实根据上下文环境和心跳事件,进行刷新服务实例ConfigClientProperties
中的云数据信息,包括配置的用户名、密码和configPath。
小结
本文主要介绍了 Spring Cloud Config 客户端实现中对于服务端配置信息变更监听 ConfigServerInstanceProvider 和 EventListener 的相关实现。
在本专题开始时的案例,实现了 config client 启动时,根据配置的规则,应用服务会先从 config Server 拉取其对应的配置信息,然后才会初始化 config client 的上下文环境。除此之外还补充了配置的动态刷新和 webHook 应用于配置刷新。下面的文章将会进入 Spring Cloud Config 的应用进阶。