apollo spring @value 源码分析
apollo和spring完美结合,自动更新估计也是反射,用起来非常舒服,推荐使用
spring的@value 初始化在下面的代码里
初始化 @value
SpringApplication
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
this.applyInitializers(context);
this.applyInitializers(context);
跟进去可以看见ApolloApplicationContextInitializer
this.initializers = {[email protected]} size = 7
0 = {[email protected]}
1 = {[email protected]}
2 = {[email protected]} --------------------- 这里会把properSource设置入spring
3 = {[email protected]}
4 = {[email protected]}
5 = {[email protected]}
6 = {[email protected]}
然后这个ApolloApplicationContextInitializer的initialize方法如下
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
String enabled = environment.getProperty("apollo.bootstrap.enabled", "false");
if (!Boolean.valueOf(enabled)) {
logger.debug("Apollo bootstrap config is not enabled for context {}, see property: ${{}}", context, "apollo.bootstrap.enabled");
} else {
logger.debug("Apollo bootstrap config is enabled for context {}", context);
this.initialize(environment);
}
}
上面方法里还有这一步environment.getPropertySources().addFirst(composite);将property设置入environment第一个
从这一步起Config config = ConfigService.getConfig(namespace);
经过一系列的操作 如下等等
return new DefaultConfig(namespace, createLocalConfigRepository(namespace));
最终进入这个方法return new RemoteConfigRepository(namespace);这是个关键类
apollo先随便理理,以后再补充吧
关键类是这个RemoteConfigRepository
关键代码是这几步骤
this.trySync();// 尝试同步配置
this.schedulePeriodicRefresh();// 初始化定时刷新配置的任务
this.scheduleLongPollingRefresh();// 注册自己到 RemoteConfigLongPollService 中,实现配置更新的实时通知
-------------------------------------------------------------------
trySync里面有个关键代码
ApolloConfig current = loadApolloConfig();
里面调接口
url = assembleQueryConfigUrl(configService.getHomepageUrl(), appId, cluster, m_namespace,
dataCenter, m_remoteMessages.get(), m_configCache.get());
HttpResponse<ApolloConfig> response = m_httpUtil.doGet(request, ApolloConfig.class);
get获取
HttpResponse<ApolloConfig> response = m_httpUtil.doGet(request, ApolloConfig.class);
其中的url类似这个
http://localhost:8080/configs/test_1/default/application.properties?ip=localhost
返回类似这个 里面已经有我设置好的值了
{
"appId": "test_1",
"cluster": "default",
"namespaceName": "application.properties",
"configurations": {
"apollo.property1": "xxx",
"apollo.percent": "444"
},
"releaseKey": "20191011143416-fe52624c5bd4583f"
}
获取的返回就是一个AplloConfig
ApolloConfig result = response.getBody();
然后设置入
m_configCache.set(current);
this.remoteConfigLongPollService.submit(this.m_namespace, this);
------------------------------------------------------------------------------
this.schedulePeriodicRefresh(); 这个定时更新就是默认每5分钟更新下trySync一下
---------------------------------------------------------------------------
this.scheduleLongPollingRefresh();这里是个长连接更新 经过下面一系列的方法
remoteConfigLongPollService.submit(m_namespace, this);
startLongPolling();
进入关键的一步
m_longPollingService.submit(new Runnable() {
@Override
public void run() {
if (longPollingInitialDelayInMills > 0) {
try {
logger.debug("Long polling will start in {} ms.", longPollingInitialDelayInMills);
TimeUnit.MILLISECONDS.sleep(longPollingInitialDelayInMills);
} catch (InterruptedException e) {
//ignore
}
}
doLongPollingRefresh(appId, cluster, dataCenter);
}
});
默认睡2秒 执行这个
doLongPollingRefresh(appId, cluster, dataCenter);
里面进去就是个死循环
while (!m_longPollingStopped.get() && !Thread.currentThread().isInterrupted()) {
里面又是个get请求
url =
assembleLongPollRefreshUrl(lastServiceDto.getHomepageUrl(), appId, cluster, dataCenter,
m_notifications);
url类似这个
http://localhost:8080/notifications/v2?cluster=default&appId=test_1&ip=localhost¬ifications=%5B%7B%22namespaceName%22%3A%22application.properties%22%2C%22notificationId%22%3A-1%7D%5D
response类似这个
[{
"namespaceName": "application",
"notificationId": 26,
"messages": {
"details": {
"test_1+default+application": 26
}
}
}]
根据返回值在进行操作 如果有改变 返回值就是200
if (response.getStatusCode() == 200 && response.getBody() != null) {
updateNotifications(response.getBody());
updateRemoteNotifications(response.getBody());
transaction.addData("Result", response.getBody().toString());
notify(lastServiceDto, response.getBody());
}
notify(lastServiceDto, response.getBody());这一步里面还有trySync操作 更新上面的RemoteConfigRepository
而且这里面有个比较诡异的点 在死循环中 如果apollo服务端没有更新的notification,客户端调用如下网址,就会超时,默认90s
估计是服务端做了控制,虽然是长连接,但是如果没有notification,还空转的话,对客户端服务端都不好,自己猜的
好,综上就是apollo能注入@value,且能及时更新的原因了,标红为关键部分
写的比较糙,自己看的
推荐阅读
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
spring源码分析系列5:ApplicationContext的初始化与Bean生命周期
-
spring源码分析6: ApplicationContext的初始化与BeanDefinition的搜集入库
-
九、Spring之BeanFactory源码分析(一)
-
Spring源码分析之IoC容器初始化
-
Spring源码分析
-
深入源码分析Spring中的构造器注入
-
深入源码分析Spring注解的实现原理---@Import
-
Spring Aop源码分析
-
spring boot 2.0 源码分析(四)