教程:一塊兒學習Hystrix--Hystrix經常使用場景--降級(回退)

目錄

  • Hystrix本系列博文
  • 靜態降級(返回默認值)
  • 自定義降級
  • 網絡緩存降級
  • 主備降級
  • 聲明

Hystrix本系列博文

    如下爲博主寫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 );
    }

自定義降級

     當命令返回的是一個包含多個字段的複合對象時,一般會使用自定義降級,其中一些字段能夠由其餘請求狀態肯定,而其餘字段設置爲默認值。

     適合使用自定義降級的例子有:

  • cookies
  • 請求參數和請求頭
  • 以前的服務請求在當前失敗以前的響應

    降級時會靜態的從請求範圍檢索出存根, 可是若是須要的話,好比下面這個例子演示了它如何處理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

相關文章
相關標籤/搜索