[Soul 源碼之旅] 1.8.7 Soul插件初體驗(yàn) (Hystrix 3)

基于上次介紹的Rxjava 的基礎(chǔ),我們今天再來(lái)聊一下 Hystrix,我們先看一下 Hystrix 整體處理流程圖:


consume

Hystrix 在設(shè)計(jì)之初主要為了解決以下問(wèn)題:分布式系統(tǒng)環(huán)境下,各個(gè)服務(wù)之間相互依賴(lài),當(dāng)其中有一些服務(wù)發(fā)生故障時(shí),依賴(lài)于這個(gè)服務(wù)的其他服務(wù)可會(huì)被這種故障影響,導(dǎo)致整個(gè)系統(tǒng)癱瘓,而 Hystrix 就是為了放置這種整體系統(tǒng)癱瘓而設(shè)計(jì)出來(lái)的,可以將爆炸半徑縮小到有問(wèn)題的服務(wù)上。我們接著看一下 Hystrix 的幾種設(shè)計(jì)目標(biāo):

  • 阻止故障發(fā)生連鎖效應(yīng),導(dǎo)致系統(tǒng)雪崩。
  • 發(fā)現(xiàn)問(wèn)題快速失敗,并能及時(shí)恢復(fù)。
  • 提供優(yōu)雅的降級(jí)策略。
  • 能夠提供實(shí)時(shí)的監(jiān)控。

1.8.7.1 Hystrix 簡(jiǎn)單示例

OrderServiceProvider 隨機(jī)超時(shí)并產(chǎn)生錯(cuò)誤

public class OrderServiceProvider {
    Random random = new Random();
    public Integer queryByOrderId() throws InterruptedException {
        int key = random.nextInt(10);
        if (key > 4){
            Thread.sleep(5000);
            throw new RuntimeException("11");
        }
        return 1;
    }
}

QueryOrderIdCommand HystrixCommand 的實(shí)現(xiàn)類(lèi),主要是 初始化 Hystrix 配置信息,還有實(shí)現(xiàn) call 和 getFallBack 方法。

public class QueryOrderIdCommand extends HystrixCommand<Integer> {
    private OrderServiceProvider orderServiceProvider;

    public QueryOrderIdCommand(OrderServiceProvider orderServiceProvider) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("orderService"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("queryByOrderId"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withCircuitBreakerRequestVolumeThreshold(10)//至少有10個(gè)請(qǐng)求,熔斷器才進(jìn)行錯(cuò)誤率的計(jì)算
                        .withCircuitBreakerSleepWindowInMilliseconds(5000)//熔斷器中斷請(qǐng)求5秒后會(huì)進(jìn)入半打開(kāi)狀態(tài),放部分流量過(guò)去重試
                        .withCircuitBreakerErrorThresholdPercentage(50)//錯(cuò)誤率達(dá)到50開(kāi)啟熔斷保護(hù)
                        .withExecutionTimeoutEnabled(true))
                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties
                        .Setter().withCoreSize(10)));
        this.orderServiceProvider = orderServiceProvider;
    }

    @Override
    protected Integer run() throws InterruptedException {
        return orderServiceProvider.queryByOrderId();
    }

    @Override
    protected Integer getFallback() {
        return -1;
    }
}

測(cè)試類(lèi)

 @Test
    public void testQueryByOrderIdCommand() {
        Integer r = new QueryOrderIdCommand(orderServiceProvider).execute();
        System.out.println("result:{}"+ r);
    }

我們結(jié)合上面的流程圖先來(lái)介紹一些 Hystrix 的整體處理流程。

  1. 構(gòu)造一個(gè) HystrixCommandHystrixObservableCommand對(duì)象,使用它對(duì)請(qǐng)求進(jìn)行封裝,我們示例中使用到了 HystrixCommand, 至于兩者的區(qū)別后面將會(huì)說(shuō)到。
  2. 調(diào)用剛才構(gòu)造的包裝類(lèi)執(zhí)行,主要有四個(gè)方法,下面也會(huì)說(shuō)到。
  3. 判斷是否走緩存,假如緩存開(kāi)啟并存有緩存就直接返回。
  4. 判斷熔斷開(kāi)關(guān)是否打開(kāi),假如打開(kāi)則直接跳到第八步,服務(wù)降級(jí)。
  5. 假如熔斷開(kāi)關(guān)沒(méi)有打開(kāi)或者是半開(kāi)狀態(tài)(一部分請(qǐng)求)請(qǐng)求進(jìn)入第五步,判斷緩存池或者信號(hào)量是否已經(jīng)滿(mǎn)了,滿(mǎn)了走到第八步(限流實(shí)現(xiàn))。
  6. 執(zhí)行HystrixObservableCommand.construct()HystrixCommand.run(),如果執(zhí)行失敗或者超時(shí),跳到第8步;否則,跳到第9步;
  7. 熔斷開(kāi)關(guān)進(jìn)行統(tǒng)計(jì),看是否需要觸發(fā)熔斷。
  8. 服務(wù)降級(jí)策略,即上面示例代碼中重寫(xiě)的 getFallBack 方法
  9. 請(qǐng)求響應(yīng)

1.8.7.3 HystrixCommand和HystrixObservableCommand

  • HystrixCommand用在依賴(lài)服務(wù)返回單個(gè)操作結(jié)果的時(shí)候。
  • HystrixObservableCommand 用在依賴(lài)服務(wù)返回多個(gè)操作結(jié)果的時(shí)候。

1.8.7.2 四種執(zhí)行方法

  • execute():使用同步阻塞的方式調(diào)用 command 中的 run 方法。
  • queue():以異步非阻塞方式執(zhí)行run(),只支持接收一個(gè)值對(duì)象。調(diào)用queue()就直接返回一個(gè)Future對(duì)象??赏ㄟ^(guò) Future.get()拿到run()的返回結(jié)果,但Future.get()是阻塞執(zhí)行的。若執(zhí)行成功,Future.get()返回單個(gè)返回值。
  • observe(): 在調(diào)用 subscribe() 前執(zhí)行run()/construct(),支持接收多個(gè)值對(duì)象,取決于發(fā)射源 (調(diào)用 Obsever.onNext () 方法)。調(diào)用observe()會(huì)返回一個(gè)hot Observable,也就是說(shuō),調(diào)用observe()自動(dòng)觸發(fā)執(zhí)行 run()/construct(),無(wú)論是否存在訂閱者。如果繼承的是HystrixCommand,hystrix會(huì)從線(xiàn)程池中取一個(gè)線(xiàn)程以非阻塞方式執(zhí)行run();如果繼承的是HystrixObservableCommand,將以調(diào)用線(xiàn)程阻塞執(zhí)行construct()。

observe()使用方法:

  1. 調(diào)用observe()會(huì)返回一個(gè)Observable對(duì)象
  2. 調(diào)用這個(gè)Observable對(duì)象的subscribe()方法完成事件注冊(cè),從而獲取結(jié)果
  • toObservable()在調(diào)用 subscribe() 的時(shí)候才調(diào)用Obsevalbe 的 run()/construct() 方法,不見(jiàn)兔子不撒鷹。支持接收多個(gè)值對(duì)象,取決于發(fā)射源 (調(diào)用 Obsever.onNext () 方法)。調(diào)用toObservable()會(huì)返回一個(gè)cold Observable,也就是說(shuō),調(diào)用toObservable()不會(huì)立即觸發(fā)執(zhí)行run()/construct(),必須有訂閱者訂閱Observable時(shí)才會(huì)執(zhí)行。
    如果繼承的是HystrixCommand,hystrix會(huì)從線(xiàn)程池中取一個(gè)線(xiàn)程以非阻塞方式執(zhí)行run(),調(diào)用線(xiàn)程不必等待run();如果繼承的是HystrixObservableCommand,將以調(diào)用線(xiàn)程堵塞執(zhí)行construct(),調(diào)用線(xiàn)程需等待construct()執(zhí)行完才能繼續(xù)往下走。

toObservable()使用方法:

  1. 調(diào)用observe()會(huì)返回一個(gè)Observable對(duì)象
  2. 調(diào)用這個(gè)Observable對(duì)象的subscribe()方法完成事件注冊(cè),從而獲取結(jié)果 。

1.8.7.4 資源隔離

Hystrix 的資源隔離主要分為兩種返回式一種是線(xiàn)程池,一種是信號(hào)量,我們下面將詳細(xì)講解一下。

1.8.7.4.1 線(xiàn)程池

我們先來(lái)看一下它實(shí)現(xiàn)的核心源碼

final static ConcurrentHashMap<String, HystrixThreadPool> threadPools = new ConcurrentHashMap<String, HystrixThreadPool>();
...
if (!threadPools.containsKey(key)) {
    threadPools.put(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));
}

根據(jù)上面代碼我們可以知道,每個(gè) commondKey 對(duì)應(yīng)的線(xiàn)程池都是獨(dú)立的,也就是當(dāng)某個(gè) commandKey 出現(xiàn)故障時(shí)候,即使它使用的線(xiàn)程池被耗盡,也不會(huì)對(duì)其他 commandKey 產(chǎn)生影響,這就起到了資源隔離的作用。
我們接下來(lái)講講其優(yōu)缺點(diǎn):

  1. 優(yōu)點(diǎn)
    • 隔離性強(qiáng),即使其中某個(gè)服務(wù)錯(cuò)誤也不會(huì)對(duì)其他服務(wù)產(chǎn)生影響。
    • 當(dāng)依賴(lài)從故障恢復(fù)正常時(shí),應(yīng)用程序會(huì)立即恢復(fù)正常的性能。
    • 可以動(dòng)態(tài)配置線(xiàn)程池信息,也就是當(dāng)發(fā)現(xiàn)某個(gè)服務(wù)故障時(shí),可以將最少的資源分配給該服務(wù)。不會(huì)造成資源浪費(fèi)
  2. 缺點(diǎn)
    • 使用線(xiàn)程池在創(chuàng)建線(xiàn)程消耗較大。
    • 使用異步模式,線(xiàn)程上下文切換造成性能損耗。
1.8.7.4.1 信號(hào)量

使用線(xiàn)程池時(shí),發(fā)送請(qǐng)求的線(xiàn)程和執(zhí)行依賴(lài)服務(wù)的線(xiàn)程不是同一個(gè),而使用信號(hào)量時(shí),發(fā)送請(qǐng)求的線(xiàn)程和執(zhí)行依賴(lài)服務(wù)的線(xiàn)程是同一個(gè),都是發(fā)起請(qǐng)求的線(xiàn)程??蛻?hù)端需向依賴(lài)服務(wù)發(fā)起請(qǐng)求時(shí),首先要獲取一個(gè)信號(hào)量才能真正發(fā)起調(diào)用,由于信號(hào)量的數(shù)量有限,當(dāng)并發(fā)請(qǐng)求量超過(guò)信號(hào)量個(gè)數(shù)時(shí),后續(xù)的請(qǐng)求都會(huì)直接拒絕,進(jìn)入fallback流程。

  1. 優(yōu)點(diǎn)
    • 輕量級(jí),不存在創(chuàng)建線(xiàn)程池和線(xiàn)程上下文切換的損耗
  2. 缺點(diǎn)
    • 信號(hào)量不支持異步,也不支持超時(shí),也就是說(shuō)當(dāng)所請(qǐng)求的服務(wù)不可用時(shí),信號(hào)量會(huì)控制超過(guò)限制的請(qǐng)求立即返回,但是已經(jīng)持有信號(hào)量的線(xiàn)程只能等待服務(wù)響應(yīng)或從超時(shí)中返回,即可能出現(xiàn)長(zhǎng)時(shí)間等待。線(xiàn)程池模式下,當(dāng)超過(guò)指定時(shí)間未響應(yīng)的服務(wù),Hystrix會(huì)通過(guò)響應(yīng)中斷的方式通知線(xiàn)程立即結(jié)束并返回。

1.8.7.5 熔斷

Hystrix 的熔斷主要流程如下圖


熔斷
  1. 調(diào)用allowRequest()判斷是否允許將請(qǐng)求提交到線(xiàn)程池,它主要由 circuitBreaker.forceOpen 這個(gè)參數(shù)控制,即是否強(qiáng)制打開(kāi)開(kāi)關(guān),我們有時(shí)候并不一定是故障,有可能存在需要緊急關(guān)停某些服務(wù)的時(shí)候。
  2. 調(diào)用isOpen()判斷熔斷器開(kāi)關(guān)是否打開(kāi),如果打開(kāi)進(jìn)入第三步,- 》如果一個(gè)周期內(nèi)總的請(qǐng)求數(shù)小于circuitBreaker.requestVolumeThreshold的值,允許請(qǐng)求放行。 !-》如果一個(gè)周期內(nèi)錯(cuò)誤率小于circuitBreaker.errorThresholdPercentage的值,允許請(qǐng)求放行。否則,打開(kāi)熔斷器開(kāi)關(guān),進(jìn)入第三步。
  3. 調(diào)用allowSingleTest()判斷是否允許單個(gè)請(qǐng)求通行,如果熔斷器打開(kāi),且距離熔斷器打開(kāi)的時(shí)間或上一次試探請(qǐng)求放行的時(shí)間超過(guò)circuitBreaker.sleepWindowInMilliseconds的值時(shí),熔斷器器進(jìn)入半開(kāi)狀態(tài),允許放行一個(gè)試探請(qǐng)求;否則,不允許放行。

1.8.7.6 回退降級(jí)

降級(jí)有如下策略

  • 快速失敗,直接拋出異常。
  • 無(wú)聲失敗,即返回一個(gè)空值
  • 靜態(tài)降級(jí),返回一個(gè)預(yù)設(shè)的靜態(tài)值。
  • cache ,返回緩存的舊的結(jié)果。

1.8.7.7 總結(jié)

今天學(xué)習(xí)完 Hystrix 我們對(duì)它實(shí)現(xiàn)的大體流程已經(jīng)清楚了,接下來(lái)我們看一下 soul 是怎么通過(guò) Hystrix 實(shí)現(xiàn)限流的。

參考文獻(xiàn): https://my.oschina.net/7001/blog/1619842

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 什么是HystrixHystrix是Spring Cloud提供的一種帶有熔斷機(jī)制的框架,由于在微服務(wù)系統(tǒng)中同一個(gè)...
    學(xué)編程的小屁孩閱讀 1,422評(píng)論 0 3
  • hystrix是什么? 在微服務(wù)場(chǎng)景中,通常會(huì)有很多層的服務(wù)調(diào)用。如果一個(gè)底層服務(wù)出現(xiàn)問(wèn)題,故障會(huì)被向上傳播給用戶(hù)...
    段永平閱讀 463評(píng)論 0 0
  • 1.3.1 Hystrix 簡(jiǎn)單介紹 hystrix 是netfix 公司開(kāi)發(fā)的防止服務(wù)雪崩的短路裝置,增加我們服...
    AndyWei123閱讀 599評(píng)論 0 1
  • 沒(méi)有使用過(guò)hystrix,需要了解hystrix原理:How-it-Works[https://github.co...
    cutieagain閱讀 304評(píng)論 0 0
  • Hystrix的用途以及使用場(chǎng)景就不在這里贅述了,這里只關(guān)注源碼級(jí)別的實(shí)現(xiàn)原理。 1、AbstractComman...
    aiwen2017閱讀 740評(píng)論 0 0

友情鏈接更多精彩內(nèi)容