如下爲博主寫Hystrix系列的文章列表,順便騙個贊,以爲寫的還能夠的,不要吝嗇你的贊喲html
點擊查看 Hystrix入門java
點擊查看 Hystrix命令執行git
點擊查看 Hystrix處理異常機制(降級方法)github
點擊查看 Hystrix命令名稱、分組、線程池redis
點擊查看 Hystrix命令名稱、Hystrix請求處理後端
點擊查看 Hystrix請求處理緩存
點擊查看 Hystrix經常使用場景--失敗服務器
能夠在代碼中靜態的返回默認值進行降級, 這不會致使功能或服務以「靜默失敗」的方式被刪除,而是致使默認行爲發生。cookie
例如,若是一個命令基於用戶憑據返回true/false,若是命令執行失敗,它能夠默認爲true:網絡
@Override protected Boolean getFallback() { return true; }
HystrixObservableCommand
等價 對於 HystrixObservableCommand
的靜默失敗解決方案是調用重寫 resumeWithFallback
方法,示例以下:
@Override protected Observable<Boolean> resumeWithFallback() { return Observable.just( true ); }
當命令返回的是一個包含多個字段的複合對象時,一般會使用自定義降級,其中一些字段能夠由其餘請求狀態肯定,而其餘字段設置爲默認值。
適合使用自定義降級的例子有:
降級時會靜態的從請求範圍檢索出存根, 可是若是須要的話,好比下面這個例子演示了它如何處理countryCodeFromGeoLookup字段,一般建議在命令實例化時注入它們。
public class HystrixStubbedFallback extends HystrixCommand<HystrixStubbedFallback.UserAccount> { private final int customerId; private final String countryCodeFromGeoLookup; /** * @param customerId 用於檢索UserAccount * @param countryCodeFromGeoLookup * 來自HTTP請求geo代碼查找的默認國家代碼用於回退。 */ protected HystrixStubbedFallback(int customerId, String countryCodeFromGeoLookup) { super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); this.customerId = customerId; this.countryCodeFromGeoLookup = countryCodeFromGeoLookup; } @Override protected UserAccount run() { // 從遠程服務獲取UserAccount // 返回 UserAccountClient.getAccount(customerId); throw new RuntimeException("forcing failure for example"); //模擬異經常使用於觸發回滾 } @Override protected UserAccount getFallback() { /** * 返回有一些靜態默認值,佔位符,還有一個注入的咱們將使用的值'countryCodeFromGeoLookup'。 * 而不是咱們從遠程服務檢索出來的 */ return new UserAccount(customerId, "Unknown Name", countryCodeFromGeoLookup, true, true, false); } public static class UserAccount { private final int customerId; private final String name; private final String countryCode; private final boolean isFeatureXPermitted; private final boolean isFeatureYPermitted; private final boolean isFeatureZPermitted; UserAccount(int customerId, String name, String countryCode, boolean isFeatureXPermitted, boolean isFeatureYPermitted, boolean isFeatureZPermitted) { this.customerId = customerId; this.name = name; this.countryCode = countryCode; this.isFeatureXPermitted = isFeatureXPermitted; this.isFeatureYPermitted = isFeatureYPermitted; this.isFeatureZPermitted = isFeatureZPermitted; } } }
下面是單元測試示範上面代碼的功能:
@Test public void test() { CommandWithStubbedFallback command = new CommandWithStubbedFallback(1234, "ca"); UserAccount account = command.execute(); assertTrue(command.isFailedExecution()); assertTrue(command.isResponseFromFallback()); assertEquals(1234, account.customerId); assertEquals("ca", account.countryCode); assertEquals(true, account.isFeatureXPermitted); assertEquals(true, account.isFeatureYPermitted); assertEquals(false, account.isFeatureZPermitted); }
HystrixObservableCommand
等價 對於 HystrixObservableCommand
的靜默失敗解決方案是調用重寫 resumeWithFallback
方法, 用於返回一個可觀察者,發送自必定的響應。與前面的例子等價的版本是這樣的:
@Override protected Observable<Boolean> resumeWithFallback() { return Observable.just( new UserAccount(customerId, "Unknown Name", countryCodeFromGeoLookup, true, true, false) ); }
可是,若是您指望從 Observable
中發出多個數據項,您可能更感興趣的是爲那些在失敗前還沒發射原始的 Observable
生成存根。這裏有一個簡單的例子來講明如何實現這一點——跟蹤主Observable
中發出的最後一個數據項,從而使回退知道在何處繼續這個順序:
@Override protected Observable<Integer> construct() { return Observable.just(1, 2, 3) .concatWith(Observable.<Integer> error(new RuntimeException("forced error"))) .doOnNext(new Action1<Integer>() { @Override public void call(Integer t1) { lastSeen = t1; } }) .subscribeOn(Schedulers.computation()); }
@Override protected Observable<Integer> resumeWithFallback() { if (lastSeen < 4) { return Observable.range(lastSeen + 1, 4 - lastSeen); } else { return Observable.empty(); } }
有時若是後端服務失敗,能夠從緩存服務(如memcached)檢索過期的數據版本。 因爲回退會超出網絡,因此它是另外一個可能的失敗點,所以它也須要被一個HystrixCommand或HystrixObservableCommand包裹。
(執行示意圖)
在單獨的線程池上執行降級(回退)命令是很重要的,不然,若是主命令被隱藏並填充線程池,由於兩個命令共享同一個池,將阻止降級(回退)。
下面將舉例怎樣使用 CommandWithFallbackViaNetwork
中的 getFallback()
方法執行經過網絡緩存降級。
注意:若是回滾失敗,將會經過返回null執行「靜默失敗」的回滾操做。
經過配置 FallbackViaNetwork
命令,使它運行在一個不一樣的線程池中,而不是來源於HystrixCommandGroupKey
的默認RemoteServiceX
,它將 HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback")
注入到構造函數中。
這樣子 CommandWithFallbackViaNetwork
將在RemoteServiceX的線程池上運行,而FallbackViaNetwork將運行在 RemoteServiceXFallback
線程池上。
示例以下:
public class HystrixFallbackViaNetwork extends HystrixCommand<String> { private final int id; protected HystrixFallbackViaNetwork(int id) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX")) .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueCommand"))); this.id = id; } @Override protected String run() { // 正常狀況執行:RemoteServiceXClient.getValue(id); throw new RuntimeException("force failure for example"); //模擬異常執行降級操做 } @Override protected String getFallback() { return new FallbackViaNetwork(id).execute(); } private static class FallbackViaNetwork extends HystrixCommand<String> { private final int id; public FallbackViaNetwork(int id) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX")) .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueFallbackCommand")) // 回滾命令使用一個不一樣的線程池,這樣不會被飽和的RemoteServiceX池子阻止執行降級 .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback"))); this.id = id; } @Override protected String run() { // MemCacheClient.getValue(id); // 正常狀況應該執行 從緩存(MemCache或者redis)中讀取結果 throw new RuntimeException("the fallback also failed"); //模擬異常,執行降級操做 } @Override protected String getFallback() { // 若是降級操做失敗則會觸發這裏進行靜態失敗降級 return null; } } }
不少系統都有雙模型--主備或者故障轉移。
有時候備機或故障轉移可能回事失敗的狀態,這種狀況下,就會像經過網絡緩存降級那種模式。然而若是切換到備機或故障轉移是常見的。 例如,推出擴展新功能(有時這是有狀態系統和處理代碼推送的一部分),而後每次使用輔助系統時,主服務器將處於故障狀態、跳閘斷路器和觸發警報。
這不是咱們但願的狀況,若是沒有其餘緣由的話,那就是避免「狼來了」的疲勞,當真正的問題發生時,它會致使警報被忽略。 因此這種狀況,解決策略是將主備的轉換視爲正常和健康的模式並放一個表象。
(流程示意圖)
主服務器和備用服務器的HystrixCommand實現是線程隔離的,由於它們可能正在執行網絡流量和業務邏輯。 它們可能都有很是不一樣的性能特徵(一般次要系統是靜態緩存),因此隔離的另外一個好處是它們能夠單獨調優。
您不會公開的顯示這兩個命令,而是將它們隱藏在另外一個HystrixCommand後面,該命令是信號隔離的,它實現了是否調用主服務器命令仍是備機命令的條件邏輯。若是主命令和備命令都失敗了,那麼控制切換到自己正面的降級。
正面的HystrixCommand可使用信號隔離,由於它所作的全部工做都是經過另外兩個已經線程隔離的hystrix命令進行的。 只要正面的HystrixCommand中 run()
方法不執行任何其餘網絡調用、重試邏輯或其餘「容易出錯」的事情,就沒有必要再使用另外一層線程了。
public class HystrixFacadeWithPrimarySecondary extends HystrixCommand<String> { private final static DynamicBooleanProperty usePrimary = DynamicPropertyFactory.getInstance().getBooleanProperty("primarySecondary.usePrimary", true); private final int id; public HystrixFacadeWithPrimarySecondary(int id) { super(Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX")) .andCommandKey(HystrixCommandKey.Factory.asKey("PrimarySecondaryCommand")) .andCommandPropertiesDefaults( // 默認想要的是信號隔離 由於已經包裝了兩個已經線程隔離的命令 HystrixCommandProperties.Setter() .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE))); this.id = id; } @Override protected String run() { if (usePrimary.get()) { return new PrimaryCommand(id).execute(); } else { return new SecondaryCommand(id).execute(); } } @Override protected String getFallback() { return "static-fallback-" + id; } @Override protected String getCacheKey() { return String.valueOf(id); } private static class PrimaryCommand extends HystrixCommand<String> { private final int id; private PrimaryCommand(int id) { super(Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX")) .andCommandKey(HystrixCommandKey.Factory.asKey("PrimaryCommand")) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("PrimaryCommand")) .andCommandPropertiesDefaults( // 主服務器超時時間默認爲600ms HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(600))); this.id = id; } @Override protected String run() { // 執行重的「主」服務調用 return "responseFromPrimary-" + id; } } private static class SecondaryCommand extends HystrixCommand<String> { private final int id; private SecondaryCommand(int id) { super(Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX")) .andCommandKey(HystrixCommandKey.Factory.asKey("SecondaryCommand")) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("SecondaryCommand")) .andCommandPropertiesDefaults( // 備服務器超時時間默認爲100ms HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(100))); this.id = id; } @Override protected String run() { // 執行快速的備服務調用 return "responseFromSecondary-" + id; } } }
轉帖請註明原貼地址:https://my.oschina.net/u/2342969/blog/1817652