Hystrix降級技術解析-Fallback

一、降級

所謂降級,就是指在在Hystrix執(zhí)行非核心鏈路功能失敗的情況下芹血,我們如何處理,比如我們返回默認值等楞慈。如果我們要回退或者降級處理幔烛,代碼上需要實現(xiàn)HystrixCommand.getFallback()方法或者是HystrixObservableCommand. HystrixObservableCommand()。

public class CommandHelloFailure extends HystrixCommand<String> {

    private final String name;

    public CommandHelloFailure(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.name = name;
    }

    @Override
    protected String run() {
        throw new RuntimeException("this command always fails");
    }

    @Override
    protected String getFallback() {
        return "Hello Failure " + name + "!";
    }
}
二囊蓝、Hystrix的降級回退方式

Hystrix一共有如下幾種降級回退模式:

1饿悬、Fail Fast 快速失敗
 @Override
    protected String run() {
        if (throwException) {
            throw new RuntimeException("failure from CommandThatFailsFast");
        } else {
            return "success";
        }
    }

如果我們實現(xiàn)的是HystrixObservableCommand.java則 重寫 resumeWithFallback方法

@Override
    protected Observable<String> resumeWithFallback() {
        if (throwException) {
            return Observable.error(new Throwable("failure from CommandThatFailsFast"));
        } else {
            return Observable.just("success");
        }
    }
2、Fail Silent 無聲失敗

返回null聚霜,空Map乡恕,空List

fail silent.png
@Override
    protected String getFallback() {
        return null;
    }
@Override
    protected List<String> getFallback() {
        return Collections.emptyList();
    }
@Override
    protected Observable<String> resumeWithFallback() {
        return Observable.empty();
    }
3、Fallback: Static 返回默認值

回退的時候返回靜態(tài)嵌入代碼中的默認值俯萎,這樣就不會導致功能以Fail Silent的方式被清楚傲宜,也就是用戶看不到任何功能了。而是按照一個默認的方式顯示夫啊。

@Override
    protected Boolean getFallback() {
        return true;
    }
@Override
    protected Observable<Boolean> resumeWithFallback() {
        return Observable.just( true );
    }
4函卒、Fallback: Stubbed 自己組裝一個值返回

當我們執(zhí)行返回的結果是一個包含多個字段的對象時,則會以Stubbed 的方式回退撇眯。Stubbed 值我們建議在實例化Command的時候就設置好一個值报嵌。以countryCodeFromGeoLookup為例,countryCodeFromGeoLookup的值熊榛,是在我們調用的時候就注冊進來初始化好的锚国。CommandWithStubbedFallback command = new CommandWithStubbedFallback(1234, "china");主要代碼如下:

public class CommandWithStubbedFallback extends HystrixCommand<UserAccount> {

protected CommandWithStubbedFallback(int customerId, String countryCodeFromGeoLookup) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.customerId = customerId;
        this.countryCodeFromGeoLookup = countryCodeFromGeoLookup;
    }
    @Override
    protected UserAccount getFallback() {
        /**
         * Return stubbed fallback with some static defaults, placeholders,
         * and an injected value 'countryCodeFromGeoLookup' that we'll use
         * instead of what we would have retrieved from the remote service.
         */
        return new UserAccount(customerId, "Unknown Name",
                countryCodeFromGeoLookup, true, true, false);
    }
5、Fallback: Cache via Network 利用遠程緩存

通過遠程緩存的方式玄坦。在失敗的情況下再發(fā)起一次remote請求血筑,不過這次請求的是一個緩存比如redis绘沉。由于是又發(fā)起一起遠程調用,所以會重新封裝一次Command豺总,這個時候要注意车伞,執(zhí)行fallback的線程一定要跟主線程區(qū)分開,也就是重新命名一個ThreadPoolKey喻喳。

Cache via Network.png
public class CommandWithFallbackViaNetwork extends HystrixCommand<String> {
    private final int id;

    protected CommandWithFallbackViaNetwork(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"))
                    // use a different threadpool for the fallback command
                    // so saturating the RemoteServiceX pool won't prevent
                    // fallbacks from executing
                    .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback")));
            this.id = id;
        }

        @Override
        protected String run() {
            MemCacheClient.getValue(id);
        }

        @Override
        protected String getFallback() {
            // the fallback also failed
            // so this fallback-of-a-fallback will 
            // fail silently and return null
            return null;
        }
    }
}
6另玖、Primary + Secondary with Fallback 主次方式回退(主要和次要)

這個有點類似我們日常開發(fā)中需要上線一個新功能,但為了防止新功能上線失敗可以回退到老的代碼表伦,我們會做一個開關比如使用zookeeper做一個配置開關谦去,可以動態(tài)切換到老代碼功能。那么Hystrix它是使用通過一個配置來在兩個command中進行切換蹦哼。

Primary + Secondary with Fallback.png
/**
 * Sample {@link HystrixCommand} pattern using a semaphore-isolated command
 * that conditionally invokes thread-isolated commands.
 */
public class CommandFacadeWithPrimarySecondary extends HystrixCommand<String> {

    private final static DynamicBooleanProperty usePrimary = DynamicPropertyFactory.getInstance().getBooleanProperty("primarySecondary.usePrimary", true);

    private final int id;

    public CommandFacadeWithPrimarySecondary(int id) {
        super(Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("PrimarySecondaryCommand"))
                .andCommandPropertiesDefaults(
                        // we want to default to semaphore-isolation since this wraps
                        // 2 others commands that are already thread isolated
                        // 采用信號量的隔離方式
                        HystrixCommandProperties.Setter()
                                .withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)));
        this.id = id;
    }

    //通過DynamicPropertyFactory來路由到不同的command
    @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(
                            // we default to a 600ms timeout for primary
                            HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(600)));
            this.id = id;
        }

        @Override
        protected String run() {
            // perform expensive 'primary' service call
            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(
                            // we default to a 100ms timeout for secondary
                            HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(100)));
            this.id = id;
        }

        @Override
        protected String run() {
            // perform fast 'secondary' service call
            return "responseFromSecondary-" + id;
        }

    }

    public static class UnitTest {

        @Test
        public void testPrimary() {
            HystrixRequestContext context = HystrixRequestContext.initializeContext();
            try {
                //將屬性"primarySecondary.usePrimary"設置為true鳄哭,則走PrimaryCommand;設置為false翔怎,則走SecondaryCommand
                ConfigurationManager.getConfigInstance().setProperty("primarySecondary.usePrimary", true);
                assertEquals("responseFromPrimary-20", new CommandFacadeWithPrimarySecondary(20).execute());
            } finally {
                context.shutdown();
                ConfigurationManager.getConfigInstance().clear();
            }
        }

        @Test
        public void testSecondary() {
            HystrixRequestContext context = HystrixRequestContext.initializeContext();
            try {
                //將屬性"primarySecondary.usePrimary"設置為true,則走PrimaryCommand杨耙;設置為false赤套,則走SecondaryCommand
                ConfigurationManager.getConfigInstance().setProperty("primarySecondary.usePrimary", false);
                assertEquals("responseFromSecondary-20", new CommandFacadeWithPrimarySecondary(20).execute());
            } finally {
                context.shutdown();
                ConfigurationManager.getConfigInstance().clear();
            }
        }
    }
}
三、總結

降級的處理方式珊膜,返回默認值容握,返回緩存里面的值(包括遠程緩存比如redis和本地緩存比如jvmcache)。
但回退的處理方式也有不適合的場景:
1车柠、寫操作
2剔氏、批處理
3、計算
以上幾種情況如果失敗竹祷,則程序就要將錯誤返回給調用者谈跛。
轉載請注明出處,并附上鏈接http://www.reibang.com/p/266cba713eb1
參考資料:https://github.com/Netflix/Hystrix/wiki

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末塑陵,一起剝皮案震驚了整個濱河市感憾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌令花,老刑警劉巖阻桅,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異兼都,居然都是意外死亡嫂沉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門扮碧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來趟章,“玉大人,你說我怎么就攤上這事∮却В” “怎么了搔啊?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長北戏。 經常有香客問我负芋,道長,這世上最難降的妖魔是什么嗜愈? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任旧蛾,我火速辦了婚禮,結果婚禮上蠕嫁,老公的妹妹穿的比我還像新娘锨天。我一直安慰自己,他們只是感情好剃毒,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布病袄。 她就那樣靜靜地躺著,像睡著了一般赘阀。 火紅的嫁衣襯著肌膚如雪益缠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天基公,我揣著相機與錄音幅慌,去河邊找鬼。 笑死轰豆,一個胖子當著我的面吹牛胰伍,可吹牛的內容都是我干的。 我是一名探鬼主播酸休,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼骂租,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了斑司?” 一聲冷哼從身側響起菩咨,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎陡厘,沒想到半個月后抽米,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡糙置,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年云茸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谤饭。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡标捺,死狀恐怖懊纳,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情亡容,我是刑警寧澤嗤疯,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站闺兢,受9級特大地震影響茂缚,放射性物質發(fā)生泄漏。R本人自食惡果不足惜屋谭,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一脚囊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧桐磁,春花似錦悔耘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至校摩,卻和暖如春看峻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秧耗。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工备籽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舶治,地道東北人分井。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像霉猛,于是被迫代替她去往敵國和親尺锚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理惜浅,服務發(fā)現(xiàn)瘫辩,斷路器,智...
    卡卡羅2017閱讀 134,659評論 18 139
  • 一坛悉、認識Hystrix Hystrix是Netflix開源的一款容錯框架伐厌,包含常用的容錯方法:線程池隔離、信號量隔...
    新棟BOOK閱讀 26,479評論 1 37
  • 一裸影、認識Hystrix Hystrix是Netflix開源的一款容錯框架挣轨,包含常用的容錯方法:線程池隔離、信號量隔...
    新棟BOOK閱讀 4,046評論 0 19
  • 詳細信息轩猩,見官方文檔卷扮。 Hystrix屬性的4中優(yōu)先級 1. 內置全局默認值(Global default fro...
    zlup閱讀 54,161評論 2 17
  • (git上的源碼:https://gitee.com/rain7564/spring_microservices_...
    sprainkle閱讀 9,356評論 13 33