客户端获取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请求。