netflix
全家桶的代码不是很优雅,各种单例,静态方法调用,hystrix
是我读过最屎的代码,兼职无力吐槽,恶心的一批。和其他开源项目项目,代码写的真的是一塌糊涂。
application.yml Hystrix 配置如何被加载
配置如何注入 Hystrix
注入流程
Hystrix
熔断可以通过在application.yml
配置相关参数,但是和常见的在application.yml
的配置参数不同,hystrix
的配置参数没有对应的@ConfigurationProperties
配置类。(在spring boot
体系下,application.yml
配置都会对应一个@ConfigurationProperties
配置类。)
在我的一番寻找下,发现了application.yml
配置如何被hystrix
加载的逻辑。
在Spring Cloud Hystrix
依赖了spring-cloud-netflix-archaius
,有一个自动配置类ArchaiusAutoConfiguration
。
其中configurableEnvironmentConfiguration
方法使用ConfigurableEnvironmentConfiguration
包装了ConfigurableEnvironment
,前者是对apache common
包的AbstractConfiguration
类的实现,后者是spring environment
。在spring boot
内,application.yml
配置都会被放在environment
对象内。
@Bean
public static ConfigurableEnvironmentConfiguration configurableEnvironmentConfiguration(ConfigurableEnvironment env, ApplicationContext context) {
Map<String, AbstractConfiguration> abstractConfigurationMap = context.getBeansOfType(AbstractConfiguration.class);
List<AbstractConfiguration> externalConfigurations = new ArrayList<>(abstractConfigurationMap.values());
ConfigurableEnvironmentConfiguration envConfig = new ConfigurableEnvironmentConfiguration(env);
// configureArchaius 方法很重要
configureArchaius(envConfig, env, externalConfigurations);
return envConfig;
}
configureArchaius(envConfig, env, externalConfigurations);
,这个方法很重要, hystrix
能够读取application.yml
的原因就是在这个方法里做到的。
protected static void configureArchaius(ConfigurableEnvironmentConfiguration envConfig, ConfigurableEnvironment env, List<AbstractConfiguration> externalConfigurations) {
if (initialized.compareAndSet(false, true)) {
ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
... // 忽略 hystrix 其他配置来源,只关注 spring environment
config.addConfiguration(envConfig,
ConfigurableEnvironmentConfiguration.class.getSimpleName());
...
addArchaiusConfiguration(config);
}
...
}
在configureArchaius
方法内,构建了一个ConcurrentCompositeConfiguration
对象,添加了hystrix
不同来源的配置,我们只关注spring environment
来源的配置。最后调用addArchaiusConfiguration
方法。
private static void addArchaiusConfiguration(ConcurrentCompositeConfiguration config) {
if (ConfigurationManager.isConfigurationInstalled()) {
...
}
else {
ConfigurationManager.install(config);
}
}
在addArchaiusConfiguration
方法内,调用了ConfigurationManager.install
方法,配置config
。
这就是为什么说hystrix
代码屎的原因,配置是通过静态方法注入的,正常的情况下谁会想到配置在这里被注入!!
在ConfigurationManager.install
方法内,通过DynamicPropertyFactory.initWithConfigurationSource(config)
方法,初始化DynamicPropertyFactory
内部配置。(这里又是静态方法初始化,一层嵌一层,还都是静态方法调用,巨辣鸡)
public static synchronized void install(AbstractConfiguration config) throws IllegalStateException {
if (!customConfigurationInstalled) {
setDirect(config);
if (DynamicPropertyFactory.getBackingConfigurationSource() != config) {
// 重要代码
DynamicPropertyFactory.initWithConfigurationSource(config);
}
} else {
throw new IllegalStateException("A non-default configuration is already installed");
}
}
initWithConfigurationSource
方法内有一个setDirect
方法,在setDirect
方法内调用了DynamicProperty.registerWithDynamicPropertySupport(support)
方法,初始化DynamicProperty
。
public static DynamicPropertyFactory initWithConfigurationSource(DynamicPropertySupport dynamicPropertySupport) {
synchronized (ConfigurationManager.class) {
...
setDirect(dynamicPropertySupport);
return instance;
}
}
static void setDirect(DynamicPropertySupport support) {
synchronized (ConfigurationManager.class) {
config = support;
DynamicProperty.registerWithDynamicPropertySupport(support);
initializedWithDefaultConfig = false;
}
}
在DynamicProperty.registerWithDynamicPropertySupport
方法内,初始化了dynamicPropertySupportImpl
对象,该对象保存了hystrix
所有不同来源的配置。
static synchronized void initialize(DynamicPropertySupport config) {
dynamicPropertySupportImpl = config;
config.addConfigurationListener(new DynamicPropertyListener());
updateAllProperties();
}
static void registerWithDynamicPropertySupport(DynamicPropertySupport config) {
initialize(config);
}
总结
上面的流程分析了spring boot application.yml
配置如何注入到hystrix
内。总结其实很简单,在自动配置类ArchaiusAutoConfiguration
内,获取spring environment
,通过一系列hystrix
相关类的static
方法,注入到DynamicProperty
对象内,由dynamicPropertySupportImpl
持有。
SpringBoot Application start
--> Bean Initialization
-->
ArchaiusAutoConfiguration configurableEnvironmentConfiguration
-->
ConfigurationManager.install(config)
-->
DynamicPropertyFactory.initWithConfigurationSource(config)
-->
DynamicPropertyFactory.initWithConfigurationSource
-->
DynamicPropertyFactory.setDirect
-->
DynamicProperty.registerWithDynamicPropertySupport(support)
配置如何被读取
此小节只关注配置hystrix
如何读取配置,hystrix
核心流程不涉及,所以直接从配置相关的流程开始分析。
在hystrix
内,针对方法的熔断配置,最后都会对应一个HystrixCommand
对象,所以从HystrixCommand
入手。
HystrixCommand
继承了AbstractCommand
,后者在构造器内初始化hystrix properties
。
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) {
...
this.properties = initCommandProperties(this.commandKey, propertiesStrategy, commandPropertiesDefaults);
...
}
在构造器内通过initCommandProperties
初始化hystrix properties
。在initCommandProperties
方法中,会调用HystrixPropertiesFactory.getCommandProperties
获取command properties
。
private static HystrixCommandProperties initCommandProperties(HystrixCommandKey commandKey, HystrixPropertiesStrategy propertiesStrategy, HystrixCommandProperties.Setter commandPropertiesDefaults) {
if (propertiesStrategy == null) {
return HystrixPropertiesFactory.getCommandProperties(commandKey, commandPropertiesDefaults);
} else {
// used for unit testing
return propertiesStrategy.getCommandProperties(commandKey, commandPropertiesDefaults);
}
}
在getCommandProperties
方法内,会调用HystrixPropertiesStrategy
对象getCommandProperties
方法。返回一个HystrixCommandProperties
对象。
public static HystrixCommandProperties getCommandProperties(HystrixCommandKey key, HystrixCommandProperties.Setter builder) {
...
if (cacheKey != null) {
...
if (properties != null) {
return properties;
} else {
...
properties = hystrixPropertiesStrategy.getCommandProperties(key, builder);
...
}
}
...
}
HystrixPropertiesStrategy.getCommandProperties
方法,返回HystrixCommandProperties
对象,该对象继承HystrixCommandProperties
类,后者包含了所有的hystrix
配置内容。
public HystrixCommandProperties getCommandProperties(HystrixCommandKey commandKey, HystrixCommandProperties.Setter builder) {
return new HystrixPropertiesCommandDefault(commandKey, builder);
}
HystrixCommandProperties
构造器内会初始化各种hystrix
配置值。
protected HystrixCommandProperties(HystrixCommandKey key, HystrixCommandProperties.Setter builder, String propertyPrefix) {
this.key = key;
this.circuitBreakerEnabled = getProperty(propertyPrefix, key, "circuitBreaker.enabled", builder.getCircuitBreakerEnabled(), default_circuitBreakerEnabled);
...
}
这些配置都通过getProperty
方法获取配置值。从getProperty
方法可以看到,hystrix command
如果配置了commandKey
,会读取commandKey
对应的配置,默认还会加上一个default
的配置。
这段代码解释了为什么我们配置default
这个commandKey
能够让所有的hystrix
生效。
private static HystrixProperty<Integer> getProperty(String propertyPrefix, HystrixCommandKey key, String instanceProperty, Integer builderOverrideValue, Integer defaultValue) {
return forInteger()
.add(propertyPrefix + ".command." + key.name() + "." + instanceProperty, builderOverrideValue)
.add(propertyPrefix + ".command.default." + instanceProperty, defaultValue)
.build();
}
forInteger
方法会返回一个ChainBuilder
对象,随后调用此对象的add
方法。又要吐槽一下hystrix
代码,这里一个add
方法,正常谁会想到add
里做了特殊的处理,配置就是在add
方法里读取的。
public ChainBuilder<T> add(String name, T defaultValue) {
properties.add(getDynamicProperty(name, defaultValue, getType()));
return this;
}
在add
方法内,会调用getDynamicProperty
方法,想不到吧! add
方法 里面还有特殊的处理逻辑。
private static <T> HystrixDynamicProperty<T> getDynamicProperty(String propName, T defaultValue, Class<T> type) {
HystrixDynamicProperties properties = HystrixPlugins.getInstance().getDynamicProperties();
HystrixDynamicProperty<T> p = HystrixDynamicProperties.Util.getProperty(properties, propName, defaultValue, type);
return p;
}
HystrixPlugins.getInstance().getDynamicProperties()
方法内部又是一堆逻辑,里面会通过SPI
方式初始化HystrixDynamicPropertiesArchaius
对象并返回。该对象继承了HystrixDynamicProperties
。
在HystrixDynamicProperties.Util.getProperty
方法内,通过代理的方式,实际根据返回类型调用对应的HystrixDynamicPropertiesArchaius.getXXX
方法。
public static <T> HystrixDynamicProperty<T> getProperty(
HystrixDynamicProperties properties, String name, T fallback, Class<T> type) {
return (HystrixDynamicProperty<T>) doProperty(properties, name, fallback, type);
}
private static HystrixDynamicProperty<?> doProperty(
HystrixDynamicProperties delegate,
String name, Object fallback, Class<?> type) {
if(type == String.class) {
return delegate.getString(name, (String) fallback);
}
else if (type == Integer.class) {
return delegate.getInteger(name, (Integer) fallback);
}
else if (type == Long.class) {
return delegate.getLong(name, (Long) fallback);
}
else if (type == Boolean.class) {
return delegate.getBoolean(name, (Boolean) fallback);
}
throw new IllegalStateException();
}
以getLong
为例,以下代码为HystrixDynamicPropertiesArchaius.getLong
代码。方法内返回一个LongDynamicProperty
对象。
@Override
public HystrixDynamicProperty<Long> getLong(String name, Long fallback) {
return new LongDynamicProperty(name, fallback);
}
根据类型不同,类似LongDynamicProperty
的类有多种,它们都继承自PropertyWrapper
类。PropertyWrapper
类构造器内,调用DynamicProperty.getInstance
,获取propName
对应的值。
protected PropertyWrapper(String propName, V defaultValue) {
this.prop = DynamicProperty.getInstance(propName);
this.defaultValue = defaultValue;
...
}
getInstance
方法内,会创建DynamicProperty
对象。
public static DynamicProperty getInstance(String propName) {
...
DynamicProperty prop = ALL_PROPS.get(propName);
if (prop == null) {
prop = new DynamicProperty(propName);
DynamicProperty oldProp = ALL_PROPS.putIfAbsent(propName, prop);
if (oldProp != null) {
prop = oldProp;
}
}
return prop;
}
DynamicProperty
构造器内,会调用updateValue
方法,方法内部会调用dynamicPropertySupportImpl.getString(propName)
获取配置值。
上一节里分析到Hystrix
初始化配置时,配置会由DynamicProperty
的dynamicPropertySupportImpl
对象持有。所以从application.yml
里注入的配置在这里被读取了。整个配置读取流程走通了。
private DynamicProperty(String propName) {
this.propName = propName;
updateValue();
}
private boolean updateValue() {
String newValue;
try {
if (dynamicPropertySupportImpl != null) {
newValue = dynamicPropertySupportImpl.getString(propName);
} else {
return false;
}
}
...
}
总结
application.yml
配置先通过spring bean
构建时期,注入到DynamicProperty
对象内,hystrix
初始化时会构建HystrixCommandProperties
对象,内部会初始化hystrix
相关配置。HystrixCommandProperties
通过一系列花式操作获取ArchaiusDynamicProperty
对象,该对象继承PropertyWrapper
,后者的构造器内会通过DynamicProperty
静态方法getInstance
获取hystrix
在application.yml
内的配置值。
hystrix
代码有点乱,不知道为什么一个配置流程封装的这么复杂,完全没必要。