在之前的文章,分享了Feign、Hystrix、Ribbon的基本原理及源码实现,其中最让人头痛的就是配置,这也是很多开发者在使用spring-cloud-netflix组件最烦心的事(netflix的Archaius和相互交织),有时候感叹:见鬼了,我的配置怎么没生效,配置读取的好像不太对....诸如此类的问题!不过随着Spring Cloud Netflix项目进入维护模式,说实话Spring Boot external config才是主流。
Feign
Feign的配置还算相当的友好,基本上都是遵循Spring Boot external config标准的,通过FeignClientProperties、FeignClientEncodingProperties、FeignHttpClientProperties实现。
##################FeignClientProperties###############
#全局默认配置名称:defalut
feign.client.config.defalut.error-decoder=com.example.feign.MyErrorDecoder
feign.client.config.defalut.connectTimeout=5000
#修改全局默认配置名称为:my-config
feign.client.default-config=my-config
feign.client.config.my-config.error-decoder=com.example.feign.MyErrorDecoder
feign.client.config.my-config.connectTimeout=5000
#局部配置,@FeignClient#name=user
feign.client.config.user.error-decoder=com.example.feign.MyErrorDecoder
feign.client.config.user.connectTimeout=5000
##################FeignHttpClientProperties##################
feign.httpclient.enabled=true
feign.httpclient.connectionTimeout=5000
##################FeignClientEncodingProperties##################
# 配置请求GZIP压缩
feign.compression.request.enabled=true
# 配置响应GZIP压缩 FeignContentGzipEncodingAutoConfiguration
feign.compression.response.enabled=true
# 配置压缩支持的MIME TYPE
feign.compression.request.mime-types=text/xml,application/xml,application/json
# 配置压缩数据大小的下限
feign.compression.request.min-request-size=2048
当同时配置了全局(默认)配置和局部配置时,局部配置最终会生效。如何验证呢?
接下来就看源码吧
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean{
//为每个contract配置Feign
protected void configureFeign(FeignContext context, Feign.Builder builder) {
FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
if (properties != null) {
if (properties.isDefaultToProperties()) {//defaultConfig = "default"
configureUsingConfiguration(context, builder);
//全局配置
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
//局部配置,有则覆盖
configureUsingProperties(properties.getConfig().get(this.name), builder);
} else {
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
configureUsingProperties(properties.getConfig().get(this.name), builder);
configureUsingConfiguration(context, builder);
}
} else {
configureUsingConfiguration(context, builder);
}
}
protected void configureUsingProperties(FeignClientProperties.FeignClientConfiguration config, Feign.Builder builder) {
if (config == null) {
return;
}
if (config.getLoggerLevel() != null) {
builder.logLevel(config.getLoggerLevel());
}
if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
builder.options(new Request.Options(config.getConnectTimeout(), config.getReadTimeout()));
}
if (config.getRetryer() != null) {
Retryer retryer = getOrInstantiate(config.getRetryer());
builder.retryer(retryer);
}
if (config.getErrorDecoder() != null) {
ErrorDecoder errorDecoder = getOrInstantiate(config.getErrorDecoder());
builder.errorDecoder(errorDecoder);
}
if (config.getRequestInterceptors() != null && !config.getRequestInterceptors().isEmpty()) {
// this will add request interceptor to builder, not replace existing
for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
RequestInterceptor interceptor = getOrInstantiate(bean);
builder.requestInterceptor(interceptor);
}
}
if (config.getDecode404() != null) {
if (config.getDecode404()) {
builder.decode404();
}
}
}
}
Hystrix
从spring boot的项目(spring cloud)的视角看(这很重要,复杂的放到最后讲),Hystrix默认配置都在HystrixCommandProperties和HystrixThreadPoolProperties类中,也不算很复杂。我们的接口名(groupKey)可能会重复,这个时候同名的接口会共用这一条Hystrix配置。
###全集配置
# 设置熔断超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
hystrix.command.default.execution.timeout.enabled=false
###局部配置
# 设置熔断超时时间
hystrix.command.hello.execution.isolation.thread.timeoutInMilliseconds=10000
# 关闭熔断功能
hystrix.command.hello.execution.timeout.enabled=false
当同时配置了默认配置和局部配置时,局部配置最终会生效。如何验证呢?
public abstract class Feign {
//commandKey的规则(之间讲过),在SetterFactory.Default调用
//比如:route53.Route53.list()将生成route53.Route53#list()
public static String configKey(Class targetType, Method method) {
StringBuilder builder = new StringBuilder();
builder.append(targetType.getSimpleName());
builder.append('#').append(method.getName()).append('(');
for (Type param : method.getGenericParameterTypes()) {
param = Types.resolve(targetType, targetType, param);
builder.append(Types.getRawType(param).getSimpleName()).append(',');
}
if (method.getParameterTypes().length > 0) {
builder.deleteCharAt(builder.length() - 1);
}
return builder.append(')').toString();
}
}
//一般情况下(之间讲过),入口在HystrixInvocationHandler
public abstract class HystrixCommand<R> extends AbstractCommand<R>{
protected HystrixCommand(Setter setter) {
// use 'null' to specify use the default
this(setter.groupKey, setter.commandKey, setter.threadPoolKey, null, null, setter.commandPropertiesDefaults, setter.threadPoolPropertiesDefaults, null, null, null, null, null);
}
HystrixCommand(HystrixCommandGroupKey group, HystrixCommandKey key, HystrixThreadPoolKey threadPoolKey, HystrixCircuitBreaker circuitBreaker, HystrixThreadPool threadPool,
HystrixCommandProperties.Setter commandPropertiesDefaults, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults,
HystrixCommandMetrics metrics, TryableSemaphore fallbackSemaphore, TryableSemaphore executionSemaphore,
HystrixPropertiesStrategy propertiesStrategy, HystrixCommandExecutionHook executionHook) {
super(group, key, threadPoolKey, circuitBreaker, threadPool, commandPropertiesDefaults, threadPoolPropertiesDefaults, metrics, fallbackSemaphore, executionSemaphore, propertiesStrategy, executionHook);
}
}
abstract class AbstractCommand<R> implements HystrixInvokableInfo<R>, HystrixObservable<R> {
protected AbstractCommand(HystrixCommandGroupKey group, HystrixCommandKey key, HystrixThreadPoolKey threadPoolKey, HystrixCircuitBreaker circuitBreaker, HystrixThreadPool threadPool,
HystrixCommandProperties.Setter commandPropertiesDefaults, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults,
HystrixCommandMetrics metrics, TryableSemaphore fallbackSemaphore, TryableSemaphore executionSemaphore,
HystrixPropertiesStrategy propertiesStrategy, HystrixCommandExecutionHook executionHook) {
/*
* Properties initialization
*/
if (propertiesStrategy == null) {
//执行这里
this.properties = HystrixPropertiesFactory.getCommandProperties(this.commandKey, commandPropertiesDefaults);
} else {
// used for unit testing
this.properties = propertiesStrategy.getCommandProperties(this.commandKey, commandPropertiesDefaults);
}
/*
* CircuitBreaker initialization
*/
if (this.properties.circuitBreakerEnabled().get()) {
if (circuitBreaker == null) {
// get the default implementation of HystrixCircuitBreaker
this.circuitBreaker = HystrixCircuitBreaker.Factory.getInstance(this.commandKey, this.commandGroup, this.properties, this.metrics);
} else {
this.circuitBreaker = circuitBreaker;
}
} else {
this.circuitBreaker = new NoOpCircuitBreaker();
}
/*
* ThreadPool initialization,这里关注下HystrixThreadPoolProperties
*/
if (threadPool == null) {
// get the default implementation of HystrixThreadPool
this.threadPool = HystrixThreadPool.Factory.getInstance(this.threadPoolKey, threadPoolPropertiesDefaults);
} else {
this.threadPool = threadPool;
}
}
}
public class HystrixPropertiesFactory {
//创建HystrixCommandProperties
public static HystrixCommandProperties getCommandProperties(HystrixCommandKey key, HystrixCommandProperties.Setter builder) {
HystrixPropertiesStrategy hystrixPropertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();
String cacheKey = hystrixPropertiesStrategy.getCommandPropertiesCacheKey(key, builder);
if (cacheKey != null) {
HystrixCommandProperties properties = commandProperties.get(cacheKey);
if (properties != null) {
return properties;
} else {
if (builder == null) {
builder = HystrixCommandProperties.Setter();
}
// create new instance,首次会执行这里,创建HystrixPropertiesCommandDefault
properties = hystrixPropertiesStrategy.getCommandProperties(key, builder);
// cache and return
HystrixCommandProperties existing = commandProperties.putIfAbsent(cacheKey, properties);
if (existing == null) {
return properties;
} else {
return existing;
}
}
} else {
// no cacheKey so we generate it with caching
return hystrixPropertiesStrategy.getCommandProperties(key, builder);
}
}
}
public class HystrixPropertiesCommandDefault extends HystrixCommandProperties {
public HystrixPropertiesCommandDefault(HystrixCommandKey key, Setter builder) {
super(key, builder);
}
}
/**
* 比如以:this.properties.circuitBreakerEnabled().get()为例
*/
public abstract class HystrixCommandProperties {
private static final Boolean default_circuitBreakerEnabled = true;
protected HystrixCommandProperties(HystrixCommandKey key, HystrixCommandProperties.Setter builder) {
//builder = HystrixCommandProperties.Setter();
this(key, builder, "hystrix");
}
protected HystrixCommandProperties(HystrixCommandKey key, HystrixCommandProperties.Setter builder, String propertyPrefix) {
this.key = key;
//看这里circuitBreakerEnabled
this.circuitBreakerEnabled = getProperty(propertyPrefix, key, "circuitBreaker.enabled", builder.getCircuitBreakerEnabled(), default_circuitBreakerEnabled);
this.circuitBreakerRequestVolumeThreshold = getProperty(propertyPrefix, key, "circuitBreaker.requestVolumeThreshold", builder.getCircuitBreakerRequestVolumeThreshold(), default_circuitBreakerRequestVolumeThreshold);
this.circuitBreakerSleepWindowInMilliseconds = getProperty(propertyPrefix, key, "circuitBreaker.sleepWindowInMilliseconds", builder.getCircuitBreakerSleepWindowInMilliseconds(), default_circuitBreakerSleepWindowInMilliseconds);
this.circuitBreakerErrorThresholdPercentage = getProperty(propertyPrefix, key, "circuitBreaker.errorThresholdPercentage", builder.getCircuitBreakerErrorThresholdPercentage(), default_circuitBreakerErrorThresholdPercentage);
this.circuitBreakerForceOpen = getProperty(propertyPrefix, key, "circuitBreaker.forceOpen", builder.getCircuitBreakerForceOpen(), default_circuitBreakerForceOpen);
this.circuitBreakerForceClosed = getProperty(propertyPrefix, key, "circuitBreaker.forceClosed", builder.getCircuitBreakerForceClosed(), default_circuitBreakerForceClosed);
this.executionIsolationStrategy = getProperty(propertyPrefix, key, "execution.isolation.strategy", builder.getExecutionIsolationStrategy(), default_executionIsolationStrategy);
//this property name is now misleading. //TODO figure out a good way to deprecate this property name
this.executionTimeoutInMilliseconds = getProperty(propertyPrefix, key, "execution.isolation.thread.timeoutInMilliseconds", builder.getExecutionIsolationThreadTimeoutInMilliseconds(), default_executionTimeoutInMilliseconds);
this.executionTimeoutEnabled = getProperty(propertyPrefix, key, "execution.timeout.enabled", builder.getExecutionTimeoutEnabled(), default_executionTimeoutEnabled);
this.executionIsolationThreadInterruptOnTimeout = getProperty(propertyPrefix, key, "execution.isolation.thread.interruptOnTimeout", builder.getExecutionIsolationThreadInterruptOnTimeout(), default_executionIsolationThreadInterruptOnTimeout);
this.executionIsolationSemaphoreMaxConcurrentRequests = getProperty(propertyPrefix, key, "execution.isolation.semaphore.maxConcurrentRequests", builder.getExecutionIsolationSemaphoreMaxConcurrentRequests(), default_executionIsolationSemaphoreMaxConcurrentRequests);
this.fallbackIsolationSemaphoreMaxConcurrentRequests = getProperty(propertyPrefix, key, "fallback.isolation.semaphore.maxConcurrentRequests", builder.getFallbackIsolationSemaphoreMaxConcurrentRequests(), default_fallbackIsolationSemaphoreMaxConcurrentRequests);
this.fallbackEnabled = getProperty(propertyPrefix, key, "fallback.enabled", builder.getFallbackEnabled(), default_fallbackEnabled);
this.metricsRollingStatisticalWindowInMilliseconds = getProperty(propertyPrefix, key, "metrics.rollingStats.timeInMilliseconds", builder.getMetricsRollingStatisticalWindowInMilliseconds(), default_metricsRollingStatisticalWindow);
this.metricsRollingStatisticalWindowBuckets = getProperty(propertyPrefix, key, "metrics.rollingStats.numBuckets", builder.getMetricsRollingStatisticalWindowBuckets(), default_metricsRollingStatisticalWindowBuckets);
this.metricsRollingPercentileEnabled = getProperty(propertyPrefix, key, "metrics.rollingPercentile.enabled", builder.getMetricsRollingPercentileEnabled(), default_metricsRollingPercentileEnabled);
this.metricsRollingPercentileWindowInMilliseconds = getProperty(propertyPrefix, key, "metrics.rollingPercentile.timeInMilliseconds", builder.getMetricsRollingPercentileWindowInMilliseconds(), default_metricsRollingPercentileWindow);
this.metricsRollingPercentileWindowBuckets = getProperty(propertyPrefix, key, "metrics.rollingPercentile.numBuckets", builder.getMetricsRollingPercentileWindowBuckets(), default_metricsRollingPercentileWindowBuckets);
this.metricsRollingPercentileBucketSize = getProperty(propertyPrefix, key, "metrics.rollingPercentile.bucketSize", builder.getMetricsRollingPercentileBucketSize(), default_metricsRollingPercentileBucketSize);
this.metricsHealthSnapshotIntervalInMilliseconds = getProperty(propertyPrefix, key, "metrics.healthSnapshot.intervalInMilliseconds", builder.getMetricsHealthSnapshotIntervalInMilliseconds(), default_metricsHealthSnapshotIntervalInMilliseconds);
this.requestCacheEnabled = getProperty(propertyPrefix, key, "requestCache.enabled", builder.getRequestCacheEnabled(), default_requestCacheEnabled);
this.requestLogEnabled = getProperty(propertyPrefix, key, "requestLog.enabled", builder.getRequestLogEnabled(), default_requestLogEnabled);
// threadpool doesn't have a global override, only instance level makes sense
this.executionIsolationThreadPoolKeyOverride = asProperty(new DynamicStringProperty(propertyPrefix + ".command." + key.name() + ".threadPoolKeyOverride", null));
}
private static HystrixProperty<Boolean> getProperty(String propertyPrefix, HystrixCommandKey key, String instanceProperty, Boolean builderOverrideValue, Boolean defaultValue) {
//第一个参数是局部配置,第二个是默认配置
return asProperty(new HystrixPropertiesChainedArchaiusProperty.BooleanProperty(
new HystrixPropertiesChainedArchaiusProperty.DynamicBooleanProperty(propertyPrefix + ".command." + key.name() + "." + instanceProperty, builderOverrideValue),
new HystrixPropertiesChainedArchaiusProperty.DynamicBooleanProperty(propertyPrefix + ".command.default." + instanceProperty, defaultValue)));
}
}
public interface HystrixProperty<T> {
public static class Factory {
//构建了一个调用链
public static <T> HystrixProperty<T> asProperty(final HystrixPropertiesChainedArchaiusProperty.ChainLink<T> chainedProperty) {
return new HystrixProperty<T>() { @Override
public T get() {
return chainedProperty.get();
}
};
}
}
}
public abstract class HystrixPropertiesChainedArchaiusProperty {
/**
* @return T
*/
public T get() {
if (pReference.get() == this) {//如果局部配置不为空,获取执行
return this.getValue();
} else {
return pReference.get().get();//默认配置
}
}
}
Ribbon
从spring boot的项目(spring cloud)的视角看(这很重要,复杂的放到最后讲),关于ribbon的配置,确实并未安Spring Boot external config要求封装,基本上都在CommonClientConfigKey中。
格式:<clientName>.<nameSpace>.NFLoadBalancerRuleClassName=xx
###全集配置就是是对所有的请求生效的,CommonClientConfigKey
# 设置连接超时时间
ribbon.ConnectTimeout=600
# 设置读取超时时间
ribbon.ReadTimeout=6000
# 对所有操作请求都进行重试
ribbon.OkToRetryOnAllOperations=true
# 切换实例的重试次数
ribbon.MaxAutoRetriesNextServer=2
# 对当前实例的重试次数
ribbon.MaxAutoRetries=1
###局部配置
# Interval to refresh the server list from the source
kxtx-oms.ribbon.ServerListRefreshInterval=2000
# Connect timeout used by Apache HttpClient
kxtx-oms.ribbon.ConnectTimeout=3000
# Read timeout used by Apache HttpClient
当然同时配置了全局(默认)配置和局部配置时,局部配置最终会生效。如何验证呢?
比如,我们以MaxAutoRetries以例,展开对源码的考究
public class DefaultLoadBalancerRetryHandler implements RetryHandler {
public DefaultLoadBalancerRetryHandler(IClientConfig clientConfig) {
//以MaxAutoRetries为突破口
this.retrySameServer = clientConfig.get(CommonClientConfigKey.MaxAutoRetries, DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES);
this.retryNextServer = clientConfig.get(CommonClientConfigKey.MaxAutoRetriesNextServer, DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER);
this.retryEnabled = clientConfig.get(CommonClientConfigKey.OkToRetryOnAllOperations, false);
}
}
public class DefaultClientConfigImpl implements IClientConfig {
//获取执行MaxAutoRetries值
@Override
public <T> T get(IClientConfigKey<T> key, T defaultValue) {
T value = get(key);
if (value == null) {
value = defaultValue;
}
return value;
}
//get(key)
@Override
public <T> T get(IClientConfigKey<T> key) {
Object obj = getProperty(key.key());
if (obj == null) {
return null;
}
Class<T> type = key.type();
if (type.isInstance(obj)) {
return type.cast(obj);
} else {
if (obj instanceof String) {
String stringValue = (String) obj;
if (Integer.class.equals(type)) {
return (T) Integer.valueOf(stringValue);
} else if (Boolean.class.equals(type)) {
return (T) Boolean.valueOf(stringValue);
} else if (Float.class.equals(type)) {
return (T) Float.valueOf(stringValue);
} else if (Long.class.equals(type)) {
return (T) Long.valueOf(stringValue);
} else if (Double.class.equals(type)) {
return (T) Double.valueOf(stringValue);
} else if (TimeUnit.class.equals(type)) {
return (T) TimeUnit.valueOf(stringValue);
}
throw new IllegalArgumentException("Unable to convert string value to desired type " + type);
}
throw new IllegalArgumentException("Unable to convert value to desired type " + type);
}
}
//getProperty(key.key())
protected Object getProperty(String key) {
if (enableDynamicProperties) {
String dynamicValue = null;
DynamicStringProperty dynamicProperty = dynamicProperties.get(key);
if (dynamicProperty != null) {
dynamicValue = dynamicProperty.get();
}
if (dynamicValue == null) {//重点从这里看
//获取局部/全局设置的值
dynamicValue = DynamicProperty.getInstance(getConfigKey(key)).getString();
if (dynamicValue == null) {//获取全局设置的值
dynamicValue = DynamicProperty.getInstance(getDefaultPropName(key)).getString();
}
}
if (dynamicValue != null) {
return dynamicValue;
}
}
return properties.get(key);
}
/*getConfigKey(key)
* 格式:<clientName>.<nameSpace>.NFLoadBalancerClassName=xx
* getNameSpace()默认为"ribbon"
*/
private String getConfigKey(String propName) {
return (clientName == null) ? getDefaultPropName(propName) : getInstancePropName(clientName, propName);
}
String getDefaultPropName(String propName) {
return getNameSpace() + "." + propName;
}
public String getInstancePropName(String restClientName, String key) {
return restClientName + "." + getNameSpace() + "." + key;
}
}
ArchaiusAutoConfiguration
ArchaiusAutoConfiguration绝对是今天要讲的重头戏!这里思考一个问题:
为何Hystrix获取属性值时频繁使用HystrixPropertiesChainedArchaiusProperty.DynamicBooleanProperty?还有就是ribbon则使用DynamicProperty.getInstance?
在application.properties中有:server.port=8080
调试(确保ArchaiusAutoConfiguration被运行后),以下两种方式都可以获取8080:
ConfigurationManager.getConfigInstance().getString("server.port")
DynamicProperty.getInstance("server.port")
那就是ArchaiusAutoConfiguration的作用(没它Hystrix和ribbon没法运行),它主要是将spring的ConfigurableEnvironment(属性配置信息)的引用都转存至ConfigurationManager.instance中了,而这个instance就是ConcurrentCompositeConfiguration,它则是包络万象(都是配置)的东西。ConfigurationManager 是进程内全局唯一,netflix组件的配置管理器。
那看源码吧
@Configuration
@ConditionalOnClass({ ConcurrentCompositeConfiguration.class,
ConfigurationBuilder.class })
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class ArchaiusAutoConfiguration {
private static final AtomicBoolean initialized = new AtomicBoolean(false);
@Autowired
private ConfigurableEnvironment env;
@Autowired(required = false)
private List<AbstractConfiguration> externalConfigurations = new ArrayList<>();
private DynamicURLConfiguration defaultURLConfig;
//重点关注这个,非常重要
@Bean
public ConfigurableEnvironmentConfiguration configurableEnvironmentConfiguration() {
ConfigurableEnvironmentConfiguration envConfig = new ConfigurableEnvironmentConfiguration(
this.env);
configureArchaius(envConfig);
return envConfig;
}
//这个是springboot监控
@Configuration
@ConditionalOnClass(Endpoint.class)
@ConditionalOnEnabledEndpoint("archaius")
protected static class ArchaiusEndpointConfiguration {
@Bean
protected ArchaiusEndpoint archaiusEndpoint() {
return new ArchaiusEndpoint();
}
}
//netflix对EnvironmentChangeEvent的响应处理机制
@Configuration
@ConditionalOnProperty(value = "archaius.propagate.environmentChangedEvent", matchIfMissing = true)
@ConditionalOnClass(EnvironmentChangeEvent.class)
protected static class PropagateEventsConfiguration
implements ApplicationListener<EnvironmentChangeEvent> {
@Autowired
private Environment env;
@Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
AbstractConfiguration manager = ConfigurationManager.getConfigInstance();
for (String key : event.getKeys()) {
for (ConfigurationListener listener : manager
.getConfigurationListeners()) {
Object source = event.getSource();
// TODO: Handle add vs set vs delete?
int type = AbstractConfiguration.EVENT_SET_PROPERTY;
String value = this.env.getProperty(key);
boolean beforeUpdate = false;
listener.configurationChanged(new ConfigurationEvent(source, type,
key, value, beforeUpdate));
}
}
}
}
protected void configureArchaius(ConfigurableEnvironmentConfiguration envConfig) {
if (initialized.compareAndSet(false, true)) {
String appName = this.env.getProperty("spring.application.name");
if (appName == null) {
appName = "application";
log.warn("No spring.application.name found, defaulting to 'application'");
}
System.setProperty(DeploymentContext.ContextKey.appId.getKey(), appName);
ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
// support to add other Configurations (Jdbc, DynamoDb, Zookeeper, jclouds,
// etc...)
if (this.externalConfigurations != null) {
for (AbstractConfiguration externalConfig : this.externalConfigurations) {
config.addConfiguration(externalConfig);
}
}
config.addConfiguration(envConfig,
ConfigurableEnvironmentConfiguration.class.getSimpleName());
defaultURLConfig = new DynamicURLConfiguration();
try {
config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME);
}
catch (Throwable ex) {
log.error("Cannot create config from " + defaultURLConfig, ex);
}
// TODO: sys/env above urls?
if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) {
SystemConfiguration sysConfig = new SystemConfiguration();
config.addConfiguration(sysConfig, SYS_CONFIG_NAME);
}
if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) {
EnvironmentConfiguration environmentConfiguration = new EnvironmentConfiguration();
config.addConfiguration(environmentConfiguration, ENV_CONFIG_NAME);
}
ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration();
config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES);
config.setContainerConfigurationIndex(
config.getIndexOfConfiguration(appOverrideConfig));
addArchaiusConfiguration(config);
}
else {
// TODO: reinstall ConfigurationManager
log.warn("Netflix ConfigurationManager has already been installed, unable to re-install");
}
}
private void addArchaiusConfiguration(ConcurrentCompositeConfiguration config) {
if (ConfigurationManager.isConfigurationInstalled()) {
AbstractConfiguration installedConfiguration = ConfigurationManager
.getConfigInstance();
if (installedConfiguration instanceof ConcurrentCompositeConfiguration) {
ConcurrentCompositeConfiguration configInstance = (ConcurrentCompositeConfiguration) installedConfiguration;
configInstance.addConfiguration(config);
}
else {
installedConfiguration.append(config);
if (!(installedConfiguration instanceof AggregatedConfiguration)) {
log.warn("Appending a configuration to an existing non-aggregated installed configuration will have no effect");
}
}
}
else {
//这个是画龙点睛之笔
ConfigurationManager.install(config);
}
}
}
总结,netflix的组件(Hystrix、Ribbon)的配置读取稍微复杂些(依赖Archaius实现,并非直接从spring中读取),这大概就是它的特点吧。