学习设计模式并不是学习新的一门语言,而是建立一种交流的共同语言和词汇,在方案设计时方便沟通,同时也帮助人们从更抽象的层次去分析问题的本质,而不被一些实现的细枝末节所困扰。
–> 返回Netflix OSS套件专栏汇总 <–
代码下载地址:https://github.com/f641385712/netflix-learning
目录
前言
了解了Hystrix
的基本情况后,接下来将逐步深入到它的使用以及原理上。
作为一个流行的开源库,扩展性、设计的弹性是必不可少的,而所谓弹性一般都通过外部化配置来实现。Hystrix
也不例外,它支持非常非常多的配置选项,对于多如牛毛的配置项,管理起来、统一实现动态化亦是一个必备的设计要素。
本文将介绍Hystrix
的属性抽象,以及和Archaius
的整合来实现配置的外部化、以及动态化~
正文
我们知道Archaius
有个属性抽象:com.netflix.config.Property
接口用于表示一个属性。而Hystrix
并没有沿用于此,而是自己抽象了一个HystrixProperty<T>
接口,用于表示Hystrix自己的一个属性。
说明:自己抽象出一个接口,而并不直接使用
Archaius
的Property
抽象,是为了降低耦合度,减少强依赖。同时也提高了自身的稳定性
HystrixProperty
它是表示属性值的通用接口,以便Hystrix可以使用属性,而不需要绑定到任何特定的支持实现。
public interface HystrixProperty<T> {
public T get();
}
对于如何得到一个HystrixProperty
实例,它提供了一个内部Factory类来做:
HystrixProperty:
public static class Factory {
// 最普通的实现:该属性木有动态性哦
public static <T> HystrixProperty<T> asProperty(final T value) {
return () -> value;
}
//==========下面方法便是和Archaius的集成,让属性具有动态性==========
// 但是请注意:下面的DynamicIntegerProperty等等API并不是Archaius的
// 而属于HystrixPropertiesChainedArchaiusProperty
// 所以其实可以看到,默认实现是使用的Archaius哦~~~~
public static HystrixProperty<Integer> asProperty(final DynamicIntegerProperty value) {
return () -> value.get();
}
... // 省略DynamicLongProperty/DynamicStringProperty...等其它类型
public static <T> HystrixProperty<T> asProperty(final HystrixProperty<T> value, final T defaultValue) { ... }
// 只要第一个不为null,就取出值
public static <T> HystrixProperty<T> asProperty(final HystrixProperty<T>... values) { ... }
public static <T> HystrixProperty<T> nullProperty() {
return () -> null;
}
}
可以看到,创建一个HystrixProperty
实例完全可以由Factory来实现,从而解耦对第三方的强制依赖。它还有个子接口HystrixDynamicProperty
,特别强调了属性的动态性。
HystrixDynamicProperty
通用接口表示一个动态属性值,以便Hystrix可以使用属性,而不绑定到任何特定的支持实现。
public interface HystrixDynamicProperty<T> extends HystrixProperty<T>{
public String getName();
// 如果属性有被更改,会执行此回调函数
public void addCallback(Runnable callback);
}
所有的HystrixDynamicProperty
实现类都是以匿名的形式呈现:分别分部在HystrixDynamicProperties
的两个实现类:HystrixDynamicPropertiesSystemProperties
和HystrixDynamicPropertiesArchaius
内部。
这就是Hystrix的属性抽象,内容蛮简单的,可以粗暴的理解为就是薄薄的一层代理而已,具体实现还得接着看下文。
和Archaius整合
根据前面所学,Archaius
是一个优秀的配置管理库,同作为自家产品,想要有外部化、动态配置的能力,没有理由不用它嘛。
当然喽,这并不是必须的,Hystrix
它并不强耦合Archaius
,而是选择了一个SPI接口:HystrixDynamicProperties
用来增加隔离性,以及可扩展性。
HystrixDynamicProperties
它是一个hystrix plugin,也就是SPI接口:允许你从不同的源里处获得configuration
,并不限定实现必须是Archaius
。
public interface HystrixDynamicProperties {
public HystrixDynamicProperty<String> getString(String name, String fallback);
public HystrixDynamicProperty<Integer> getInteger(String name, Integer fallback);
public HystrixDynamicProperty<Long> getLong(String name, Long fallback);
public HystrixDynamicProperty<Boolean> getBoolean(String name, Boolean fallback);
}
同时,为了保持调用方法的统一性,它提供了一个工具类:
HystrixDynamicProperties:
public static class Util {
// 这样出口方法可以保持统一:都叫getProperty
// 需要注意的是:实际它就是简单的delegate.getString/getInteger/...
// 所以它仅支持上述的四种类型,否则抛出IllegalStateException异常
public static <T> HystrixDynamicProperty<T> getProperty(HystrixDynamicProperties delegate, String name, T fallback, Class<T> type) {
return (HystrixDynamicProperty<T>) doProperty(properties, name, fallback, type);
}
}
在实际获取属性中,推荐使用Util#getProperty()
方法哦~。
既然它是个SPI,那必然有具体实现方式。Hystrix
中对此提供了两种实现:
HystrixDynamicPropertiesSystemProperties
HystrixDynamicPropertiesArchaius
当然后续你还会看到机遇ServiceLoader
的实现~
HystrixDynamicPropertiesSystemProperties
顾名思义,这些属性全部来自于System.getProperty()
,所以天然具有动态性,但是它有个很大缺点是:无法执行回调callback,并且还无法使用外部化配置。
HystrixDynamicPropertiesSystemProperties
是单例方式提供服务,具体实现代码非常简单,无非是System.getProperty(xxx)
而已嘛,因此本处省略源码部分。
HystrixDynamicPropertiesArchaius
底层是用com.netflix.config.PropertyWrapper
来实现属性的动态性。同时Javadoc里也说明了:只要本类在classpath下,那最终就会使用它作为HystrixDynamicProperties
SPI的实现,Archaius
被加载集成。
public class HystrixDynamicPropertiesArchaius implements HystrixDynamicProperties {
@Override
public HystrixDynamicProperty<String> getString(String name, String fallback) {
return new StringDynamicProperty(name, fallback);
}
...
// ========基于PropertyWrapper的实现=======
private abstract static class ArchaiusDynamicProperty<T> extends PropertyWrapper<T> implements HystrixDynamicProperty<T> {
protected ArchaiusDynamicProperty(String propName, T defaultValue) {
super(propName, defaultValue);
}
// 该方法是HystrixProperty接口的
@Override
public T get() {
return getValue();
}
}
// ===========具体类型的实现==========
private static class StringDynamicProperty extends ArchaiusDynamicProperty<String> {
protected StringDynamicProperty(String propName, String defaultValue) {
super(propName, defaultValue);
}
// 动态获取属性值
@Override
public String getValue() {
return prop.getString(defaultValue);
}
}
...
}
关于SPI实现的加载,可参见下面这个帮助类:HystrixArchaiusHelper
的处理。另外,竟然是SPI,那一般会使用到ServiceLoader
等加载机制,后面你就能看到Hystrix也有相关的处理方式。
HystrixArchaiusHelper
它是一个工具类,用于完成Hystrix
和Archaius
的集成,它内部会使用一些Archaius
的核心API:
class HystrixArchaiusHelper {
// 懒加载
// 对于那些在类路径中有Archaius **但选择不使用它的类**
// 要保持类装入最少。所以按需加载
// ConfigurationManager是Archaius的核心API
private static class LazyHolder {
// Method方法:此方法用于加载xxx.proerties资源,下面附带解释一把
private final static Method loadCascadedPropertiesFromResources;
private final static String CONFIG_MANAGER_CLASS = "com.netflix.config.ConfigurationManager";
// 静态代码块,给属性赋值
static {
Method load = null;
try {
Class<?> configManager = Class.forName(CONFIG_MANAGER_CLASS);
load = configManager.getMethod("loadCascadedPropertiesFromResources", String.class);
} catch (Exception e) { }
loadCascadedPropertiesFromResources = load;
}
}
// 判断:Archaius的1.x版本是否已经准备好了
static boolean isArchaiusV1Available() {
return LazyHolder.loadCascadedPropertiesFromResources != null;
}
// 使用这个Method,加载内容到Configuration里来
static void loadCascadedPropertiesFromResources(String name) {
if (isArchaiusV1Available()) {
LazyHolder.loadCascadedPropertiesFromResources.invoke(null, name);
}
}
}
以上代码主要是从classpath里找到ConfigurationManager#loadCascadedPropertiesFromResources(configName)
这个method,该method负责对属性资源进行加载:
ConfigurationManager:
// Cascaded:往下的,倾泻; 流注
public static void loadCascadedPropertiesFromResources(String configName) throws IOException {
Properties props = loadCascadedProperties(configName);
...
ConfigurationUtils.loadProperties(props, instance);
}
这个方法会根据configName
去找到对应的Properties文件,具体逻辑步骤如下:
说明:此处以configName的值等于
app
为例,进行阐述
- 先加载
app.application
文件 - 在获取到部署环境值,若不为空的话(比如环境名为test),就加载
app-test.application
这个文件,并且若出现相同key,后者覆盖前者- 环境是由
DeploymentContext#getDeploymentEnvironment()
来决定的,可以为null
- 环境是由
这就是这个Method的作用:用于加载属性文件内容到Configuration
里。同时会提供一个方法,用于创建一个HystrixDynamicProperties
实例(当然是基于archaius的):
HystrixArchaiusHelper:
static HystrixDynamicProperties createArchaiusDynamicProperties() {
if (isArchaiusV1Available()) {
loadCascadedPropertiesFromResources("hystrix-plugins");
try {
// HystrixDynamicPropertiesArchaius的全类名
Class<?> defaultProperties = Class.forName("com.netflix.hystrix.strategy.properties.archaius" + ".HystrixDynamicPropertiesArchaius");
return (HystrixDynamicProperties) defaultProperties.newInstance();
} ...
}
// Fallback to System properties.
return null;
}
该方法会创建一个HystrixDynamicPropertiesArchaius
实例,并且加载名为hystrix-plugins.properties、hystrix-plugins-环境名.properties
的属性文件到全局配置里。
综上,确切的说:Hystrix
是通过HystrixDynamicPropertiesArchaius
完成和Archaius
的整合的。
使用示例
@Test
public void fun1() throws InterruptedException {
HystrixPlugins instance = HystrixPlugins.getInstance();
HystrixDynamicProperties dynamicProperties = instance.getDynamicProperties();
System.out.println(dynamicProperties.getClass());
// dynamicProperties.getString()
HystrixDynamicProperty<String> nameProperty = HystrixDynamicProperties.Util.getProperty(dynamicProperties, "name", "defaultValue", String.class);
nameProperty.addCallback(() -> {
String name = nameProperty.getName(); // 属性名
System.out.println("属性" + name + "发生了变更");
});
System.out.println(nameProperty.get());
// hold住主线程
while (true) {
TimeUnit.SECONDS.sleep(60); // 因为poll轮询默认是60秒check一次
System.out.println(nameProperty.get());
}
}
准备一个属性文件,名为:hystrix-plugins.properties
:
name=YourBatman
运行程序,控制台输出:
YourBatman
YourBatman
YourBatman
YourBatman
...
what?竟然没有动态化???
为何动态化没生效?
例子中,要想它生效,其实很简单:把它写进名为:config.properties
文件里,即可动态化了。
再次运行程序,控制台输出:
class com.netflix.hystrix.strategy.properties.archaius.HystrixDynamicPropertiesArchaius
YourBatman
属性name发生了变更
YourBatman-Changed
表面上看,仅仅是文件名不同,效果却大不一样。而实际上背后是它的加载原理,这在前面Archaius
正解里有详细描述,这里简单复习一下:
- 在
Archaius
中,仅仅是PolledConfigurationSource
的配置元,才会有动态性 URLConfigurationSource
是该接口的一个实现类,所以关联到的属性文件均具有动态性。而它默认关联的文件名是:config.properties
- 在Spring Boot环境其实你可以把它改为
application.properties
也是可行的~
- 在Spring Boot环境其实你可以把它改为
ConfigurationManager
管理的是一个ConcurrentCompositeConfiguration
组合配置,而这个组合配置就含有DynamicURLConfiguration
( + 系统属性)
综上可知,这就是为何默认config.properties
文件的属性是具有动态性的原因。而hystrix-plugins.properties
它是被ConfigurationManager#loadCascadedPropertiesFromResources()
加载的,所以仅仅只是属性生效而已,并不具有动态性。
实际生产中,名hystrix-plugins.properties
的属性文件并不是给你配置其它属性的,从命名中你就知道:它给你配置插件用,也就是SPI使用的,后面会再次提到它。
再次强调:生产环境,请勿在名为
hystrix-plugins.properties
的文件里配置业务属性,避免不必要的干扰
总结
关于Netflix Hystrix属性抽象以及和Archaius整合实现配置外部化、动态化就介绍到这了,本文旨在让你认识到Hystrix
如何管理器属性Property,以及和Archaius
整合使得具有动态化的。
从源码处其实能看出很多有趣的小设计:自己抽象Property接口、自己抽象动态属性接口就是为了不绑定具体实现,这是一种良好的设计思想,是可以肯定和学习的。
关注A哥
Author | A哥(YourBatman) |
---|---|
个人站点 | www.yourbatman.cn |
yourbatman@qq.com | |
微 信 | fsx641385712 |
活跃平台 | ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
公众号 | BAT的乌托邦(ID:BAT-utopia) |
知识星球 | BAT的乌托邦 |
每日文章推荐 | 每日文章推荐 |