分布式配置中心Apollo源码分析
1.前言
在日常开发过程中,通常会把经常发生变更的配置放在分布式配置中心apollo中。一天突然有一个疑问,应用配置文件中和Apollo都存在相同的配置,为什么会使用apollo上的配置,而非使用应用配置文件中的配置,带着这个疑问,开启了一场阅读apollo源码的旅途。
其实这个问题在SpringBoot配置你不知道的秘密这篇文章中可以找到答案,apollo配置原理和spring.config.additional-location
属性原理类似,只要把配置放入Environment
的最前面就可以实现。
2.apollo应用初始化
在apollo客户端jar包的spring.factories
文件中可以看到这样的配置
ApolloApplicationContextInitializer
作为apollo应用上下文的初始化类,实现了ApplicationContextInitializer
接口,表明着会在SpringBoot
的启动过程中会进行初始化
2.1 阅读注释
在阅读源代码之前,先来看看相关注释,作为代码的灵魂,可以在阅读源码的过程中为我们指引方向
2.2 初始化
要想开启apollo功能,必须要指定apollo.bootstrap.enabled
属性
通过分析可以了解到初始化过程可以分为5个步骤:
- 读取指定命名空间
- 遍历命名空间
- 读取命名空间中的配置
- 将读取到的配置组装成
PropertySource
- 将
PropertySource
添加到environment
对象中
Environment
对象中维护了一个private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
列表,读取配置的时候会遍历列表从前往后进行读取,读取到结果直接返回。这也就可以说明apollo上和本地文件中有相同的配置时,读取的时候会读取apollo上的配置而不读取本地配置的原因。
3.读取配置
配置读取其实就是客户端向服务器端拉取配置的一个过程,客户端告诉服务器端自己需要哪个应用(app.id) + 哪个集群(default) + 哪个命名空间(namespace)下的配置,服务器端从表中读取满足条件的配置,然后返回给客户端,客户端缓存在内存中,方便后续频繁读取。
整体就是这样的一个思路,下面就来看看具体的代码实现,主要实现类RemoteConfigRepository
3.1 主动同步配置
3.1.1 查询服务信息
向服务端发送一个http请求,服务端从注册中心中拉取对应服务注册表返回给客户端
客户端拿着返回的服务地址信息就可以进行下一步操作
3.1.2 拉取配置
随机向服务列表中的一台服务发起请求
告诉服务器端要拉取配置对应的app.id + cluster + namespace,服务器端从对应的表中拉取最新符合条件的记录
3.2 长轮询服务刷新配置
a
客户端会向服务器端发起一个请求,服务器端发现客户端的配置不是最新的,返回200给客户端,客户端再向服务器端发起拉取配置请求;服务器端发现客户端的配置已经是最新的了,返回304,客户端不会向服务器端发送拉取配置请求
3.2.1 长连接实现
那么服务器端是如何实现长连接的呢?使用DeferredResult
挂起请求
服务器端将客户端对命名空间的请求都存放在Map中
private final Multimap<String, DeferredResultWrapper> deferredResults =
Multimaps.synchronizedSetMultimap(TreeMultimap.create(String.CASE_INSENSITIVE_ORDER, Ordering.natural()));
复制代码
服务器端发现有最新结果,就设置结果,此时服务器端会将结果响应给客户端;没有结果请求就被挂起,在60s内服务器端通过handleMessage
设置了结果,客户端就可以拿到正常响应结果,然后向服务器端发起拉取最新配置请求;在60s内服务器端没有设置结果,客户端会拿到304响应结果,此结果表示服务器端没有最新的配置,客户端不需要向服务器端发起拉取配置请求。
长连接的好处就是服务器端有最新的配置,可以及时通知客户端向服务器端拉取最新配置。
3.3 周期性主动同步配置
apollo除了依靠长连接通知客户端拉取配置外,还会周期性每隔5分钟主动向服务器端发送拉取最新配置的请求,从而保证推送不可用的情况下,可以通过主动拉取的方式来获取最新配置数据。
转载自 https://juejin.cn/post/6887125578525671431