[享学Netflix] 十八、Hystrix配置之:全局配置和实例配置
- 2020 年 3 月 18 日
- 筆記
今天,多少孩子既要美国式的自由,又要中国式的宠爱。却没有美国孩子的独立,却又失去了中国传统的孝道。 代码下载地址:https://github.com/f641385712/netflix-learning
目录
前言
上篇文章介绍了Hystrix
和Archaius
的整合,对深入了解Hystrix
内部的属性配置打好了基础。对于属性配置,程序员们的感受可能是可能既爱又恨,因为那些浩如烟海的配置项确实可能已经超过了你的脑容量。
但越是这样的工作,就越不可能靠强记的,而是应掌握其规律,学会查“字典”才是永恒之道。Hystrix
的属性配置不在少数,但它管理得非常的好,因此本文将从全局配置和实例配置作为切入点,授之以渔帮小伙伴们从根本上掌握Hystrix
的配置相关知识点。
正文
配置虽不可或缺,但也切勿让繁杂的配置扰乱了你的心智,所以从根本上找到管理配置的规律,将为你编码、调优过程大大减负。
HystrixPropertiesChainedProperty.ChainBuilder
在正式接触Hystrix
的配置管理类之前,有必要先对基础支撑组件做些了解。
ChainBuilder
用于快速构建一个HystrixDynamicProperty
实例。上篇文章已经说过HystrixDynamicProperty
的实例均以匿名方式实现,并没有可以给你直接使用的实例。因此若你想在“外部”需要构建它,还得自己写个实现类着实麻烦,因此就有了本构建器来帮你解决难题。
它是HystrixPropertiesChainedProperty
的一个静态public内部类:
HystrixPropertiesChainedProperty: public static abstract class ChainBuilder<T> { // 构建所需的所有的动态属性们 private List<HystrixDynamicProperty<T>> properties = new ArrayList<>(); // 添加一个动态属性property public ChainBuilder<T> add(HystrixDynamicProperty<T> property) { properties.add(property); return this; } // 这里最重要的方法是:getDynamicProperty(),它会自己动态去获取 // 另外注意:这个getType是个抽象方法~~~~ public ChainBuilder<T> add(String name, T defaultValue) { properties.add(getDynamicProperty(name, defaultValue, getType())); return this; } protected abstract Class<T> getType(); // 构建 // 简单的说:若properties这个List一个值都木有,就抛错 // 若只有一个值,那便是它 // 若有多个值(比如你add了多次),那就按照你add的顺序,以第一个不为null的为准 public HystrixDynamicProperty<T> build() { ... } } // HystrixPlugins.getInstance().getDynamicProperties() // 这句最终得到的是一个`HystrixDynamicPropertiesArchaius`,也就是从全局的Configuration去获取值 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; }
使用它,不仅可以快速构建出一个HystrixDynamicProperty
动态属性实例,而且还能和全局的Configuration完成整合,并且控制add()的优先级,功能还是挺强大的,推荐使用。
ChainBuilder
自己是个抽象类,那么如何构建它呢?这里提供有4个static工具方法供以使用:
HystrixPropertiesChainedProperty: public static ChainBuilder<String> forString() { return forType(String.class); } public static ChainBuilder<Integer> forInteger() { return forType(Integer.class); } public static ChainBuilder<Boolean> forBoolean() { return forType(Boolean.class); } public static ChainBuilder<Long> forLong() { return forType(Long.class); }
使用示例
@Test public void fun2() { HystrixDynamicProperty<String> property = HystrixPropertiesChainedProperty.forString() .add("hystrix.command.myApp.personName", null) .add("hystrix.command.default.personName", "name-default") .build(); System.out.println(property.get()); }
配置书写在config.properties
里,这段代码会根据配置的不同而结果不同:
- 不写任何配置,输出:
name-default
- 注意:因为第一个add()最终值是null(不管是获取,还是默认值均为null嘛),所以就以第二个值为准啦
- 书写配置如下,输出:
James
hystrix.command.myApp.personName=James hystrix.command.default.personName=Bryant
- 书写配置如下,输出:
Bryant
hystrix.command.default.personName=Bryant
这段示例代码,便是Hystrix实现动态配置,并且实现全局 + 实例配置相结合的缩影。
说明:Hystrix为几乎所有的key,即可配置一个全局配置,又可以单独为某个
HystrixCommand
单独配置一个个性化数值,弹性非常强
HystrixKey
一个接口,代表Hystrix的一个key。
public interface HystrixKey { // “name”这个词代替了“key”,这样enum就可以实现这个接口,并且它可以本地工作。 // 所以这个考量其实还蛮巧妙的,考虑挺周全 String name(); }
这个接口极其简单,它有如下实现类:

先看Default默认实现:
HystrixKey: // 注意:它还是个抽象类呢(当然接口内的肯定是static喽) abstract class HystrixKeyDefault implements HystrixKey { private final String name; public HystrixKeyDefault(String name) { this.name = name; } @Override public String name() { return name; } @Override public String toString() { return name; } }
这个抽象默认实现就太简单了,其它实现也均继承于它。
HystrixCommandGroupKey
HystrixKey
的子接口。表示HystrixCommand
的组名,用于将报告、告警、仪表盘dashboards等等信息分组放在一起。
// 该接口木有任何新增方法,只是提供了一个内部Factory类 public interface HystrixCommandGroupKey extends HystrixKey { // 接口内部类 class Factory { // 用于intern缓存HystrixCommandGroupDefault实例 // 这样我们就不会为了同一个键重复创建它们数百万次,提高效率 // InternMap它就是一个Map,内部持有ConcurrentHashMap用作缓存使用~ // 这样:每一个String类型的key,调用interned()方法后就会被缓存进Map里~~~ // 注意:这个只是私有方法,供内部方法使用 private static final InternMap<String, HystrixCommandGroupDefault> intern = new InternMap<>(key -> new HystrixCommandGroupDefault(key)); // 根据一个字符串的key,得到一个HystrixCommandGroupKey实例 public static HystrixCommandGroupKey asKey(String name) { return intern.interned(name); } // 私有,私有静态内部类实现HystrixCommandGroupKey 接口~~~ // 注意:该类私有,外部并不能访问和构造 private static class HystrixCommandGroupDefault extends HystrixKey.HystrixKeyDefault implements HystrixCommandGroupKey { public HystrixCommandGroupDefault(String name) { super(name); } } // 获取key的总数 static int getGroupCount() { return intern.size(); } } }
此接口内聚性非常高:外部可访问的只能是接口,实现类都放在了内部,并且仅仅只提供asKey()
一个方法供以使用,因此使用起来也是极其方便的设计。
另外两个子接口HystrixThreadPoolKey
以及HystrixCommandKey
的逻辑和上面一毛一样,略过。
HystrixCommandProperties
本文将以HystrixCommandProperties
为例,逐步学习Hystrix
对配置的管理,掌握了它后,其它自然就触类旁通。
HystrixCommandProperties
用于配置一个HystrixCommand
实例的配置类,含有很多配置项,默认使用archaius
管理。
public abstract class HystrixCommandProperties { ... // 非常多的成员变量HystrixProperty,每个都代表着一个属性,后面会逐个解释 // 构造器,key是必须的,其它都非必传(这个key蛮关键的) // 前缀均写死为:hystrix,一般也请不要改变它 protected HystrixCommandProperties(HystrixCommandKey key) { this(key, new Setter(), "hystrix"); } protected HystrixCommandProperties(HystrixCommandKey key, HystrixCommandProperties.Setter builder) { this(key, builder, "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()方法赋值 ... // 线程池无全局配置,只能配实例级别的 this.executionIsolationThreadPoolKeyOverride = forString().add(propertyPrefix + ".command." + key.name() + ".threadPoolKeyOverride", null).build(); } ... ... ... // 省略所有的成员属性的get方法 }
该抽象类集中管理了配置,并且很比较巧妙的实现了默认值、外部化、动态化配置的一体化实现。
全局配置和实例配置
Hystrix
它支持全局配置和实例配置,核心处理逻辑如下代码,其中最为关键之地在于它的getProperty()
这个处理方法,它会通过此方法给每个成员属性赋值。
HystrixCommandProperties: // 得到一个Boolean类型的ChainBuilder构建出一个属性值HystrixProperty // 实际build的是个HystrixDynamicProperty动态属性哦~~~~ private static HystrixProperty<Boolean> getProperty(String propertyPrefix, HystrixCommandKey key, String instanceProperty, Boolean builderOverrideValue, Boolean defaultValue) { return forBoolean() // .add(propertyPrefix + ".command." + key.name() + "." + instanceProperty, builderOverrideValue) .add(propertyPrefix + ".command.default." + instanceProperty, defaultValue) .build(); } ... // 省略String、Integer等类型
有了文首对ChainBuilder
内容的铺垫,理解这段代码就毫无障碍了,规则如下:
- Hystrix属性配置分为全局配置和实例配置
- 全局配置作用与所有的
HystrixCommand
实例,而实例配置仅作用于指定名称的HystrixCommand
实例(实例名称就是HystrixCommandKey key
) - 全局配置示例:
hystrix.command.MyInstanceName.circuitBreaker.enabled = false
- 此处的
MyInstanceName
就代表实例名称,该配置只会作用于指定的实例
- 此处的
- 实例配置示例:
hystrix.command.circuitBreaker.enabled = true
- 当全局和实例配置均能在某一实例上生效时,实例配置优先级更高
特殊案例说明
有两个稍微特殊点的案例这里做特别说明。
executionIsolationThreadPoolKeyOverride
:
this.executionIsolationThreadPoolKeyOverride = forString().add(propertyPrefix + ".command." + key.name() + ".threadPoolKeyOverride", null).build();
因为它不能有全局配置,所以只会有实例配置,譬如:hystrix.command.MyInstanceName.threadPoolKeyOverride = myThreadPool
executionIsolationStrategy
:执行时的隔离策略。因为隔离策略只能有如下两个取值:
HystrixCommandProperties: // 内部静态枚举类:执行时隔离的两种策略 // THREAD:在隔离的线程里执行run方法,并且使用线程池来限制并发的大小 // SEMAPHORE:就在当前线程里执行run方法,但是会使用全局的Semaphore信号量来控制并发 public static enum ExecutionIsolationStrategy { THREAD, SEMAPHORE }
所以这个配置它对value取值是有要求的:依赖于ExecutionIsolationStrategy.valueOf(value)
,只有它不抱错才算有效,否则配置无效会被忽略。譬如:hystrix.command.MyInstanceName.execution.isolation.strategy = SEMAPHORE
是一个合法的value值~
说明:这个value值不能是小写,比如全大写。也就是说可能取值有且仅有两个:
THREAD
和SEMAPHORE
,这点上Hystrix的容错性做得似乎不是特别的好~
Setter
Hystrix
里大量的使用内部类Setter来表示作用于其上的配置,用户可构建它的实例而通过编码的方式自定义属性值。

HystrixCommandProperties.Setter
自然也不例外,也是这个作用。
HystrixCommandProperties: public static class Setter { private Boolean circuitBreakerEnabled = null; ... private ExecutionIsolationStrategy executionIsolationStrategy = null; ... private Boolean requestLogEnabled = null; ... // 省略所有get、set方法 }
它的作用就是对外API,方便你使用API方式定制参数,形如这样使用:
@Test public void fun4(){ // 使用API方式定制配置 HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter(); setter.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD) .withExecutionTimeoutEnabled(true) .withExecutionTimeoutInMilliseconds(3000); // HystrixPropertiesStrategy HystrixCommandProperties hystrixProperties = new HystrixPropertiesCommandDefault(HystrixCommandKey.Factory.asKey("MyInstanceName"), setter); // ... 省略应用hystrixProperties的步骤喽~ }
一般情况下,直接使用Setter的方式极少,但是在API集成方面就必须使用到它了。例如Hystrix
和Feign
集成时,Setter就起到了配置桥梁的作用,不容忽视。
其它xxxProperties
像截图中的其它配置类,如HystrixThreadPoolProperties
、HystrixTimerThreadPoolProperties
、HystrixCollapserProperties
等,原理和处理方式和上面的HystrixCommandProperties
一毛一样,唯一区别就是key的中间部分不一样:
- HystrixThreadPoolProperties:
.threadpool.
/.threadpool.default.
- HystrixTimerThreadPoolProperties:
.timer.threadpool.default.
- HystrixCollapserProperties:
.collapser.
/ .collapser.default.
其它部分此处就不做过多展开了,略。
使用示例
HystrixCommandProperties
是个抽象类,并不能直接使用。像这种配置抽象类均由一个默认实现类:
public class HystrixPropertiesCommandDefault extends HystrixCommandProperties { // 只提供给你一个构造器,并没有想让你去自定义前缀的意思。固定值是:hystrix public HystrixPropertiesCommandDefault(HystrixCommandKey key, Setter builder) { super(key, builder); } }
吐槽一句:写这个类名的那个人估计当时睡着了,
HystrixPropertiesCommand
是个什么鬼,命名是HystrixCommandProperties
嘛~
因此,可以这么来用:
@Test public void fun5() { // 请注意:这里传null会抛出异常,Hystrix很多代码的健壮性其实是非常不够的,这是它的缺点,需要批评 // HystrixCommandProperties commandProperties = new HystrixPropertiesCommandDefault(HystrixCommandKey.Factory.asKey("myApp"), null); HystrixCommandProperties commandProperties = new HystrixPropertiesCommandDefault(HystrixCommandKey.Factory.asKey("myApp"), HystrixCommandProperties.Setter()); // 很明显,这里打印的肯定就是属性的默认值喽 System.out.println(commandProperties.circuitBreakerEnabled().get()); System.out.println(commandProperties.executionIsolationStrategy().get()); System.out.println(commandProperties.executionTimeoutEnabled().get()); System.out.println(commandProperties.executionTimeoutInMilliseconds().get()); }
控制台打印:
true THREAD true 1000
总结
关于Netflix Hystrix配置之:全局配置和实例配置就介绍到这了。我十分相信你看完此篇文章之后,再也不会惧怕那种浩如烟海的配置了,至少不会再担心Hystrix
不知道如何配了吧。
本文只讲述了Hystrix
如何管理配置,以及全局配置和实例配置的优先级关系等等,但是“内容”,也就是每个配置到底什么意思,能起什么作用,这在实战中还是蛮有意义的,因此在后续文章中还会详细这块内容。虽然很麻烦,但还是得做呀~