欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

apollo spring @value 源码分析

程序员文章站 2022-07-12 11:55:07
...

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&notifications=%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

http://localhost:8080/notifications/v2?cluster=default&appId=test_1&ip=localhost&notifications=%5B%7B"namespaceName"%3A"application.properties"%2C"notificationId"%3A29%7D%2C%7B"namespaceName"%3A"application"%2C"notificationId"%3A29%7D%5D

估计是服务端做了控制,虽然是长连接,但是如果没有notification,还空转的话,对客户端服务端都不好,自己猜的

好,综上就是apollo能注入@value,且能及时更新的原因了,标红为关键部分

写的比较糙,自己看的