spring cloud的Feign Hystrix Ribbon配置详解及原理

在之前的文章,分享了Feign、Hystrix、Ribbon的基本原理及源码实现,其中最让人头痛的就是配置,这也是很多开发者在使用spring-cloud-netflix组件最烦心的事(netflix的Archaius和相互交织),有时候感叹:见鬼了,我的配置怎么没生效,配置读取的好像不太对....诸如此类的问题!不过随着Spring Cloud Netflix项目进入维护模式,说实话Spring Boot external config才是主流。

Feign

Feign的配置还算相当的友好,基本上都是遵循Spring Boot external config标准的,通过FeignClientPropertiesFeignClientEncodingPropertiesFeignHttpClientProperties实现。

##################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默认配置都在HystrixCommandPropertiesHystrixThreadPoolProperties类中,也不算很复杂。我们的接口名(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中读取),这大概就是它的特点吧。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值