hystrix原理

一、hystrix 產(chǎn)生背景

微服務(wù)是解決復(fù)雜服務(wù)的一個(gè)方案,在功能不變的情況下,對(duì)一個(gè)復(fù)雜的單體服務(wù)分解為多個(gè)可管理的分支。每個(gè)服務(wù)作為輕量的子服務(wù),通過(guò)RPC實(shí)現(xiàn)服務(wù)間的關(guān)聯(lián),將服務(wù)簡(jiǎn)單化。每個(gè)服務(wù)根據(jù)自己的需要選擇技術(shù)棧,互不影響,方便開(kāi)發(fā)、維護(hù)。例如S劃分為a,b,c。微服務(wù)的好處是有效的拆分應(yīng)用,實(shí)現(xiàn)敏捷開(kāi)發(fā)和部署。
微服務(wù)一系列優(yōu)勢(shì)下,也給微服務(wù)的管理和穩(wěn)定性帶來(lái)挑戰(zhàn),比如一個(gè)服務(wù)依賴30個(gè)微服務(wù),每個(gè)微服務(wù)的可用性是99.999%,在不加任何管理的情況下,該聚合服務(wù)的可用性將是99.999%的30次方=99.97%,系統(tǒng)的可用性直接降了兩個(gè)數(shù)量級(jí)達(dá)到三個(gè)九。
且由于依賴的傳遞性,很容易產(chǎn)生雪崩效應(yīng)。如下圖所示:

Paste_Image.png
Paste_Image.png
Paste_Image.png

一個(gè)應(yīng)用中,任意一個(gè)點(diǎn)的不可用或者響應(yīng)延時(shí)都有可能造成服務(wù)不可用
更可怕的是,被hang住的請(qǐng)求會(huì)很快耗盡系統(tǒng)的資源,當(dāng)該類(lèi)請(qǐng)求越來(lái)越多,占用的計(jì)算機(jī)資源越來(lái)越多的時(shí)候,會(huì)導(dǎo)致系統(tǒng)瓶頸出現(xiàn),造成其他的請(qǐng)求同樣不可用,最終導(dǎo)致業(yè)務(wù)系統(tǒng)崩潰,又稱:雪崩效應(yīng)
造成雪崩原因可以歸結(jié)為以下三個(gè):

  • 服務(wù)提供者不可用(硬件故障,程序Bug,緩存擊穿,用戶大量請(qǐng)求)
  • 重試加大流量(用戶重試,代碼邏輯重試)
  • 服務(wù)調(diào)用者不可用(同步等待造成的資源耗盡)
  • 最終的結(jié)果就是一個(gè)服務(wù)不可用導(dǎo)致一系列服務(wù)的不可用,而往往這種后果往往無(wú)法預(yù)料的。

二、 hystrix實(shí)現(xiàn)原理

hystrix語(yǔ)義為“豪豬”,具有自我保護(hù)的能力。hystrix的出現(xiàn)即為解決雪崩效應(yīng),它通過(guò)四個(gè)方面的機(jī)制來(lái)解決這個(gè)問(wèn)題

  • 隔離(線程池隔離和信號(hào)量隔離):限制調(diào)用分布式服務(wù)的資源使用,某一個(gè)調(diào)用的服務(wù)出現(xiàn)問(wèn)題不會(huì)影響其他服務(wù)調(diào)用。
  • 優(yōu)雅的降級(jí)機(jī)制:超時(shí)降級(jí)、資源不足時(shí)(線程或信號(hào)量)降級(jí),降級(jí)后可以配合降級(jí)接口返回托底數(shù)據(jù)。
  • 融斷:當(dāng)失敗率達(dá)到閥值自動(dòng)觸發(fā)降級(jí)(如因網(wǎng)絡(luò)故障/超時(shí)造成的失敗率高),熔斷器觸發(fā)的快速失敗會(huì)進(jìn)行快速恢復(fù)。
  • 緩存:提供了請(qǐng)求緩存、請(qǐng)求合并實(shí)現(xiàn)。
  • 支持實(shí)時(shí)監(jiān)控、報(bào)警、控制(修改配置)

2.1 隔離

Paste_Image.png

(1)線程池隔離模式:使用一個(gè)線程池來(lái)存儲(chǔ)當(dāng)前的請(qǐng)求,線程池對(duì)請(qǐng)求作處理,設(shè)置任務(wù)返回處理超時(shí)時(shí)間,堆積的請(qǐng)求堆積入線程池隊(duì)列。這種方式需要為每個(gè)依賴的服務(wù)申請(qǐng)線程池,有一定的資源消耗,好處是可以應(yīng)對(duì)突發(fā)流量(流量洪峰來(lái)臨時(shí),處理不完可將數(shù)據(jù)存儲(chǔ)到線程池隊(duì)里慢慢處理)
(2)信號(hào)量隔離模式:使用一個(gè)原子計(jì)數(shù)器(或信號(hào)量)來(lái)記錄當(dāng)前有多少個(gè)線程在運(yùn)行,請(qǐng)求來(lái)先判斷計(jì)數(shù)器的數(shù)值,若超過(guò)設(shè)置的最大線程個(gè)數(shù)則丟棄改類(lèi)型的新請(qǐng)求,若不超過(guò)則執(zhí)行計(jì)數(shù)操作請(qǐng)求來(lái)計(jì)數(shù)器+1,請(qǐng)求返回計(jì)數(shù)器-1。這種方式是嚴(yán)格的控制線程且立即返回模式,無(wú)法應(yīng)對(duì)突發(fā)流量(流量洪峰來(lái)臨時(shí),處理的線程超過(guò)數(shù)量,其他的請(qǐng)求會(huì)直接返回,不繼續(xù)去請(qǐng)求依賴的服務(wù))

區(qū)別(兩種隔離方式只能選其一):

線程池隔離 信號(hào)量隔離
線程 與調(diào)用線程非相同線程 與調(diào)用線程相同(jetty線程)
開(kāi)銷(xiāo) 排隊(duì)、調(diào)度、上下文開(kāi)銷(xiāo)等 無(wú)線程切換,開(kāi)銷(xiāo)低
異步 支持 不支持
并發(fā)支持 支持(最大線程池大?。?/td> 支持(最大信號(hào)量上限)

2.2 融斷

正常狀態(tài)下,電路處于關(guān)閉狀態(tài)(Closed),如果調(diào)用持續(xù)出錯(cuò)或者超時(shí),電路被打開(kāi)進(jìn)入熔斷狀態(tài)(Open),后續(xù)一段時(shí)間內(nèi)的所有調(diào)用都會(huì)被拒絕(Fail Fast),一段時(shí)間以后,保護(hù)器會(huì)嘗試進(jìn)入半熔斷狀態(tài)(Half-Open),允許少量請(qǐng)求進(jìn)來(lái)嘗試,如果調(diào)用仍然失敗,則回到熔斷狀態(tài),如果調(diào)用成功,則回到電路閉合狀態(tài);

Paste_Image.png

HystrixCircuitBreaker(斷路器的具體實(shí)現(xiàn)):

Paste_Image.png

詳細(xì)的工作流程:http://hot66hot.iteye.com/blog/2155036

2.3 降級(jí)

可能大家會(huì)混淆“融斷”和“降級(jí)”兩個(gè)概念。
在股票市場(chǎng),熔斷這個(gè)詞大家都不陌生,是指當(dāng)股指波幅達(dá)到某個(gè)點(diǎn)后,交易所為控制風(fēng)險(xiǎn)采取的暫停交易措施。相應(yīng)的,服務(wù)熔斷一般是指軟件系統(tǒng)中,由于某些原因使得服務(wù)出現(xiàn)了過(guò)載現(xiàn)象,為防止造成整個(gè)系統(tǒng)故障,從而采用的一種保護(hù)措施,所以很多地方把熔斷亦稱為過(guò)載保護(hù)。
大家都見(jiàn)過(guò)女生旅行吧,大號(hào)的旅行箱是必備物,平常走走近處綽綽有余,但一旦出個(gè)遠(yuǎn)門(mén),再大的箱子都白搭了,怎么辦呢?常見(jiàn)的情景就是把物品拿出來(lái)分分堆,比了又比,最后一些非必需品的就忍痛放下了,等到下次箱子夠用了,再帶上用一用。而服務(wù)降級(jí),就是這么回事,整體資源快不夠了,忍痛將某些服務(wù)先關(guān)掉,待渡過(guò)難關(guān),再開(kāi)啟回來(lái)。
二者的目標(biāo)是一致的,目的都是保證上游服務(wù)的穩(wěn)定性。但其關(guān)注的重點(diǎn)并不一樣,融斷對(duì)下層依賴的服務(wù)并不級(jí)(或者說(shuō)孰輕孰重),一旦產(chǎn)生故障就斷掉;而降級(jí)需要對(duì)下層依賴的業(yè)務(wù)分級(jí),把產(chǎn)生故障的丟了,換一個(gè)輕量級(jí)的方案,是一種退而求其次的方法。
根據(jù)業(yè)務(wù)場(chǎng)景的不同,一般采用以下兩種模式:
第一種(最常用)如果服務(wù)失敗,則我們通過(guò)fallback進(jìn)行降級(jí),返回靜態(tài)值。

Paste_Image.png

第二種采用服務(wù)級(jí)聯(lián)的模式,如果第一個(gè)服務(wù)失敗,則調(diào)用備用服務(wù),例如失敗重試或者訪問(wèn)緩存失敗再去取數(shù)據(jù)庫(kù)。服務(wù)級(jí)聯(lián)的目的則是盡最大努力保證返回?cái)?shù)據(jù)的成功性,但如果考慮不充分,則有可能導(dǎo)致級(jí)聯(lián)的服務(wù)崩潰(比如,緩存失敗了,把全部流量打到數(shù)據(jù)庫(kù),瞬間導(dǎo)致數(shù)據(jù)庫(kù)掛掉)。因此級(jí)聯(lián)模式,也要慎用,增加了管理的難度。

Paste_Image.png

2.4 緩存

不建議使用,對(duì)問(wèn)題排查會(huì)造成很大的困擾,因此也不在這里講了

三、hystrix應(yīng)用

hystrix的運(yùn)行流程如下所示:

Paste_Image.png
  • 兩個(gè)核心代理HystrixCommand,HystrixObservableCommand,任何依賴的服務(wù)只需要繼承這兩個(gè)類(lèi)就可以了。其中HystrixObservableCommand使用觀察者模式(不在此介紹范圍之內(nèi),了解請(qǐng)移步RxJava)
  • HystrixCommand 可以采用同步調(diào)用和異步調(diào)用,異步返回Future對(duì)象(還未直接支持CompletebleFuture)
    如果開(kāi)啟了緩存,則會(huì)根據(jù)GroupKey,Commandkey以及cachedKey確定是否存在緩存(不建議使用)
  • 判斷斷路器是否開(kāi)啟,開(kāi)啟則直接調(diào)用getFallback,
  • 判斷是否滿足信號(hào)量隔離或線程池隔離的條件,如果隔離則拋異常
  • 執(zhí)行run方法
  • metrics包含了一個(gè)計(jì)數(shù)器,用來(lái)計(jì)算當(dāng)前服務(wù)的狀態(tài),無(wú)論是成功調(diào)用,還是拋異常都會(huì)記錄數(shù)據(jù)(接下來(lái)再詳細(xì)講)
  • 執(zhí)行降級(jí)策略

3.1 代碼實(shí)現(xiàn)


public class GetInfoFromSinaiCommand extends HystrixCommand<List<PoiInfo>> {
    private PoiClient poiClient;
    private List<Integer> poiIds;
    private static final List<String> FIELDS = ImmutableList.of("id", "cate", "subcate");

    public GetInfoFromSinaiCommand(PoiClient poiClient, List<Integer> poiIds) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("sinai"))
                //command配置
                .andCommandKey(HystrixCommandKey.Factory.asKey("GetInfoFromSinaiCommand"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withRequestCacheEnabled(true))

                //融斷器配置
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withCircuitBreakerEnabled(true))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withCircuitBreakerRequestVolumeThreshold(20))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withCircuitBreakerSleepWindowInMilliseconds(5000))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withCircuitBreakerErrorThresholdPercentage(50))

                //ThreadPool配置
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("GetInfoFromSinaiCommand"))
                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(10))
                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(-1))

        );



        this.poiClient = poiClient;
        this.poiIds = poiIds;

    }

    @Override
    public List<PoiInfo> run() throws Exception {
        if (poiIds.isEmpty()) {
            return Lists.newArrayList();
        }
        List<PoiModel> pioModels = poiClient.listPois(poiIds, FIELDS);
        return parseResult(pioModels);
    }

    @Override
    protected String getCacheKey() {
        return String.valueOf(poiIds);
    }

    @Override
    protected List<PoiInfo> getFallback() {
        return Lists.newArrayList();
    }

    private List<PoiInfo> parseResult(List<PoiModel> poiModels) {
        if (poiModels == null || poiModels.isEmpty()) {
            return Lists.newArrayList();
        }
        List<PoiInfo> res = Lists.newArrayList();
        for (PoiModel poiModel : poiModels) {
            PoiInfo poiInfo = new PoiInfo();
            poiInfo.setPoiId(poiModel.getId());

            if (poiModel.getCate() != null) {
                poiInfo.setCate(poiModel.getCate());
            }
            if (poiModel.getSubcate() != null) {
                poiInfo.setSubcate(poiModel.getSubcate());
            }
            res.add(poiInfo);
        }
        return res;
    }
}

3.2 參數(shù)說(shuō)明

|參數(shù)類(lèi)型|參數(shù)名|默認(rèn)值|說(shuō)明|
|---|---|---|---|---|
|command配置|executionIsolationStrategy|ExecutionIsolationStrategy.THREAD|信號(hào)隔離或線程隔離,默認(rèn):采用線程隔離,|
|| executionIsolationThreadTimeoutInMillisecond |1s|隔離時(shí)間大,即多長(zhǎng)時(shí)間后進(jìn)行重試|
|| executionIsolationSemaphoreMaxConcurrentRequests |10|使用信號(hào)量隔離時(shí),命令調(diào)用最大的并發(fā)數(shù),默認(rèn):10 |
| |fallbackIsolationSemaphoreMaxConcurrentRequests |10|使用信號(hào)量隔離時(shí),命令fallback(降級(jí))調(diào)用最大的并發(fā)數(shù),默認(rèn):10|
|| fallbackEnabled |true|是否開(kāi)啟fallback降級(jí)策略|
|| executionIsolationThreadInterruptOnTimeout |true|使用線程隔離時(shí),是否對(duì)命令執(zhí)行超時(shí)的線程調(diào)用中斷(Thread.interrupt())操作|
|| metricsRollingStatisticalWindowInMilliseconds |10000ms|統(tǒng)計(jì)滾動(dòng)的時(shí)間窗口,默認(rèn):10s|
|| metricsRollingStatisticalWindowBuckets |10|統(tǒng)計(jì)窗口的Buckets的數(shù)量,默認(rèn):10個(gè)
|| metricsRollingPercentileEnabled |true|是否開(kāi)啟監(jiān)控統(tǒng)計(jì)功能,默認(rèn):true|
|| requestLogEnabled |true|是否開(kāi)啟請(qǐng)求日志|
|| requestCacheEnabled |true|是否開(kāi)啟請(qǐng)求緩存|
|熔斷器配置|circuitBreakerRequestVolumeThreshold|20|主要用在小流量|
|| circuitBreakerSleepWindowInMilliseconds | 5000ms |熔斷器默認(rèn)工作時(shí)間,默認(rèn):5秒.熔斷器中斷請(qǐng)求5秒后會(huì)進(jìn)入半打開(kāi)狀態(tài),放部分流量過(guò)去重試|
|| circuitBreakerEnabled | true |是否啟用熔斷器,默認(rèn)true. 啟動(dòng) |
|| circuitBreakerErrorThresholdPercentage | 50 |默認(rèn):50%。當(dāng)出錯(cuò)率超過(guò)50%后熔斷器啟動(dòng)|
|| circuitBreakerForceOpen | false |是否強(qiáng)制開(kāi)啟熔斷器阻斷所有請(qǐng)求,默認(rèn):false,不開(kāi)啟|
|| circuitBreakerForceClosed | false |是否允許熔斷器忽略錯(cuò)誤,默認(rèn)false, 不開(kāi)啟|
|線程池配置|HystrixThreadPoolProperties.Setter().withCoreSize(int value)|10|配置線程池大小,默認(rèn)值10個(gè)|
||HystrixThreadPoolProperties.Setter().withMaxQueueSize(int value)|-1|配置線程值等待隊(duì)列長(zhǎng)度|

3.3監(jiān)控上報(bào)

參考文章:

本文的很多圖和文字都粘貼自網(wǎng)上文章,沒(méi)有注明引用請(qǐng)包涵!如有任何問(wèn)題請(qǐng)留言或者加群,我會(huì)及時(shí)回復(fù)
http://zhuanlan.51cto.com/art/201704/536307.htm

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評(píng)論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,348評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,083評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,706評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,442評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,802評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,983評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,542評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,287評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,486評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,710評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,116評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,412評(píng)論 1 294
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,224評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,462評(píng)論 2 378

推薦閱讀更多精彩內(nèi)容