apollo获取配置

客户端获取Namespace配置

默认的namespace配置获取
Config config = ConfigService.getAppConfig();
Integer defaultRequestTimeout = 200;
Integer requestTimeout = config.getIntProperty("requestTimeout", defaultRequestTimeout);
自定义namespace的配置获取
Config config = ConfigService.getConfig("FX.Hermes.Producer");
Integer defaultSenderBatchSize = 200;
Integer senderBatchSize = config.getIntProperty("sender.batchsize", defaultSenderBatchSize);

客户端监听Namespace配置变化

默认的namespace配置的监听
Config config = ConfigService.getAppConfig();
config.addChangeListener(new ConfigChangeListener() {
  @Override
  public void onChange(ConfigChangeEvent changeEvent) {
    for (String key : changeEvent.changedKeys()) {
      ConfigChange change = changeEvent.getChange(key);
      System.out.println(String.format(
        "Found change - key: %s, oldValue: %s, newValue: %s, changeType: %s",
        change.getPropertyName(), change.getOldValue(),
        change.getNewValue(), change.getChangeType()));
     }
  }
});
自定义namespace配置的监听
Config config = ConfigService.getConfig("FX.Hermes.Producer");
config.addChangeListener(new ConfigChangeListener() {
  @Override
  public void onChange(ConfigChangeEvent changeEvent) {
    System.out.println("Changes for namespace " + changeEvent.getNamespace());
    for (String key : changeEvent.changedKeys()) {
      ConfigChange change = changeEvent.getChange(key);
      System.out.println(String.format(
        "Found change - key: %s, oldValue: %s, newValue: %s, changeType: %s",
        change.getPropertyName(), change.getOldValue(),
        change.getNewValue(), change.getChangeType()));
     }
  }
});

Spring集成样例

Apollo和Spring也可以很方便地集成,只需要标注@EnableApolloConfig后就可以通过@Value获取配置信息:
@Configuration
@EnableApolloConfig
public class AppConfig {}
-------------------------------------------------------------------------------------
@Component
public class SomeBean {
    @Value("${request.timeout:200}")
    private int timeout;

    @ApolloConfigChangeListener
    private void someChangeHandler(ConfigChangeEvent changeEvent) {
        if (changeEvent.isChanged("request.timeout")) {
            refreshTimeout();
        }
    }
}
-------------------------------------------------------------------------------------
@Configuration
@EnableApolloConfig("FX.Hermes.Producer")
public class AppConfig {}
-------------------------------------------------------------------------------------
@Component
public class SomeBean {
    @Value("${request.timeout:200}")
    private int timeout;

    @ApolloConfigChangeListener("FX.Hermes.Producer")
    private void someChangeHandler(ConfigChangeEvent changeEvent) {
        if (changeEvent.isChanged("request.timeout")) {
            refreshTimeout();
        }
    }
}

应用自身配置的获取规则(namespace=default)

  当应用使用下面的语句获取配置时,我们称之为获取应用自身的配置,也就是应用自身的application namespace的配置。

Config config = ConfigService.getAppConfig();

对这种情况的配置获取规则,简而言之如下:

  • 首先查找运行时cluster的配置(通过apollo.cluster指定)
  • 如果没有找到,则查找数据中心cluster的配置
  • 如果还是没有找到,则返回默认cluster的配置
配置查找顺序

  所以如果应用部署在A数据中心,但是用户没有在Apollo创建cluster,那么获取的配置就是默认cluster(default)的。

  如果应用部署在A数据中心,同时在运行时指定了SomeCluster,但是没有在Apollo创建cluster,那么获取的配置就是A数据中心cluster的配置,如果A数据中心cluster没有配置的话,那么获取的配置就是默认cluster(default)的。

公共组件配置的获取规则

  以FX.Hermes.Producer为例,hermes producer是hermes发布的公共组件。当使用下面的语句获取配置时,我们称之为获取公共组件的配置。

Config config = ConfigService.getConfig("FX.Hermes.Producer");

对这种情况的配置获取规则,简而言之如下:

  • 首先获取当前应用下的FX.Hermes.Producer namespace的配置
  • 然后获取hermes应用下FX.Hermes.Producer namespace的配置
  • 上面两部分配置的并集就是最终使用的配置,如有key一样的部分,以当前应用优先


    配置查找顺序

      通过这种方式,就实现了对框架类组件的配置管理,框架组件提供方提供配置的默认值,应用如果有特殊需求,可以自行覆盖。

配置更新推送实现

  前面提到了Apollo客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。长连接实际上我们是通过Http Long Polling实现的,具体而言:

  • 客户端发起一个Http请求到服务端
  • 服务端会保持住这个连接30秒
    • 如果在30秒内有客户端关心的配置变化,被保持住的客户端请求会立即返回,并告知客户端有配置变化的namespace信息,客户端会据此拉取对应namespace的最新配置
    • 如果在30秒内没有客户端关心的配置变化,那么会返回Http状态码304给客户端
  • 客户端在收到服务端请求后会立即重新发起连接,回到第一步

  考虑到会有数万客户端向服务端发起长连,在服务端我们使用了async servlet(Spring DeferredResult)来服务Http Long Polling请求。

推荐阅读更多精彩内容