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 = {ArrayList@3947}  size = 7
 0 = {DelegatingApplicationContextInitializer@3957} 
 1 = {SharedMetadataReaderFactoryContextInitializer@3958} 
 2 = {ApolloApplicationContextInitializer@3959} --------------------- 這裏會把properSource設置入spring
 3 = {ContextIdApplicationContextInitializer@3960} 
 4 = {ConfigurationWarningsApplicationContextInitializer@3961} 
 5 = {ServerPortInfoApplicationContextInitializer@3962} 
 6 = {ConditionEvaluationReportLoggingListener@3963} 

 

然後這個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,且能及時更新的原因了,標紅爲關鍵部分

寫的比較糙,自己看的

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章