[享學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
如何管理配置,以及全局配置和實例配置的優先順序關係等等,但是「內容」,也就是每個配置到底什麼意思,能起什麼作用,這在實戰中還是蠻有意義的,因此在後續文章中還會詳細這塊內容。雖然很麻煩,但還是得做呀~