Hystrix源碼分析(一)

一杜跷、開篇

hystix相信大家都不陌生。github地址:https://github.com/Netflix/Hystrix解取。中文名稱翻譯為刺猬存谎,顧明思議是用來保護我們系統(tǒng)的。在分布式系統(tǒng)中可能會依賴很多服務变勇,當依賴的服務出現(xiàn)異常恤左,接口時延上漲贴唇,超時,很有可能會把上游業(yè)務的接口給拖死飞袋,把線程資源耗盡戳气。我們需要一種機制對依賴服務的可用性做分析,如果依賴服務的失敗率異常巧鸭,能夠做到類似保險絲的作用瓶您,把流量切斷,避免產生更嚴重的故障纲仍。
其中最核心的組建就是里面的斷路器呀袱。我們主要分析兩點:

  • 何時決定把斷路器打開
  • 當依賴服務恢復的時候如何自動恢復
二、源碼分析
1. 整體流程
整體流程.png

我們借用hystrix wiki上的一張圖來簡單了解整個流程郑叠。本次關注的核心點是4,7夜赵。也就是斷路器的實現(xiàn)邏輯。

2. 斷路器實現(xiàn)

斷路器的接口:HystrixCircuitBreaker

/**
 * Circuit-breaker logic that is hooked into {@link HystrixCommand} execution and will stop allowing executions if failures have gone past the defined threshold.
 * <p>
 * The default (and only) implementation  will then allow a single retry after a defined sleepWindow until the execution
 * succeeds at which point it will again close the circuit and allow executions again.
 */
public interface HystrixCircuitBreaker {

    /**
     * Every {@link HystrixCommand} requests asks this if it is allowed to proceed or not.  It is idempotent and does
     * not modify any internal state, and takes into account the half-open logic which allows some requests through
     * after the circuit has been opened
     * 
     * @return boolean whether a request should be permitted
     */
    boolean allowRequest();

    /**
     * Whether the circuit is currently open (tripped).
     * 
     * @return boolean state of circuit breaker
     */
    boolean isOpen();

    /**
     * Invoked on successful executions from {@link HystrixCommand} as part of feedback mechanism when in a half-open state.
     */
    void markSuccess();

    /**
     * Invoked on unsuccessful executions from {@link HystrixCommand} as part of feedback mechanism when in a half-open state.
     */
    void markNonSuccess();

    /**
     * Invoked at start of command execution to attempt an execution.  This is non-idempotent - it may modify internal
     * state.
     */
    boolean attemptExecution();

我們重點關注兩個方法allowRequest和isOpen乡革,分別是判斷是否允許流量進來和斷路器開啟關閉的核心接口寇僧。
HystrixCircuitBreaker有兩個實現(xiàn)類。分別是:

  • NoOpCircuitBreaker
    空實現(xiàn)類
  • HystrixCircuitBreakerImpl
    默認實現(xiàn)類沸版。本次分析的重點就是這個類嘁傀。
3. 調用棧
調用棧.png

我們重點看下applyHystrixSemantics這個方法。

    private Observable<R> applyHystrixSemantics(final AbstractCommand<R> _cmd) {
        // mark that we're starting execution on the ExecutionHook
        // if this hook throws an exception, then a fast-fail occurs with no fallback.  No state is left inconsistent
        executionHook.onStart(_cmd);

        /* determine if we're allowed to execute */
        if (circuitBreaker.allowRequest()) {
            ...
        } else {
            return handleShortCircuitViaFallback();
        }
    }

通過斷路器控制是否應該走正常的調用邏輯推穷。

4. 斷路器判斷邏輯
@Override
        public boolean allowRequest() {
            if (properties.circuitBreakerForceOpen().get()) {
                // properties have asked us to force the circuit open so we will allow NO requests
                return false;
            }
            if (properties.circuitBreakerForceClosed().get()) {
                // we still want to allow isOpen() to perform it's calculations so we simulate normal behavior
                isOpen();
                // properties have asked us to ignore errors so we will ignore the results of isOpen and just allow all traffic through
                return true;
            }
            return !isOpen() || allowSingleTest();
        }
  • 若斷路器關閉心包,則允許訪問。
  • 否則嘗試放行一部分流量進來驗收依賴服務是否正常
    接下來看isOpen的實現(xiàn)方法馒铃。
@Override
        public boolean isOpen() {
            if (circuitOpen.get()) {
                // if we're open we immediately return true and don't bother attempting to 'close' ourself as that is left to allowSingleTest and a subsequent successful test to close
                return true;
            }

            // we're closed, so let's see if errors have made us so we should trip the circuit open
            HealthCounts health = metrics.getHealthCounts();

- 請求總數(shù)沒有達到設置的請求閾值蟹腾,不會打開斷路器。(對于請求太少的場景区宇,失敗率沒有太大意義)
            // check if we are past the statisticalWindowVolumeThreshold
            if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
                // we are not past the minimum volume threshold for the statisticalWindow so we'll return false immediately and not calculate anything
                return false;
            }
- 當失敗率大于某個閾值的時候娃殖,把斷路器打開。
            if (health.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
                return false;
            } else {
- 這里要考慮并發(fā)的場景议谷,所以使用CAS的操作
                // our failure rate is too high, trip the circuit
                if (circuitOpen.compareAndSet(false, true)) {
                    // if the previousValue was false then we want to set the currentTime
- 設置斷路器的開啟時間是為了讓服務在一定的時間范圍內接受少量的流量來決定是否需要把斷路器重新關閉炉爆。                    
circuitOpenedOrLastTestedTime.set(System.currentTimeMillis());
                    return true;
                } else {
                    // How could previousValue be true? If another thread was going through this code at the same time a race-condition could have
                    // caused another thread to set it to true already even though we were in the process of doing the same
                    // In this case, we know the circuit is open, so let the other thread set the currentTime and report back that the circuit is open
                    return true;
                }
            }
        }

    }

isOpen的邏輯很清晰,簡而言之就是當失敗率大于某個閾值的時候會把斷路器打開卧晓。
接下來我們重點看下allowSingleTest的方法芬首。

public boolean allowSingleTest() {
            long timeCircuitOpenedOrWasLastTested = circuitOpenedOrLastTestedTime.get();
            // 1) if the circuit is open
            // 2) and it's been longer than 'sleepWindow' since we opened the circuit
            if (circuitOpen.get() && System.currentTimeMillis() > timeCircuitOpenedOrWasLastTested + properties.circuitBreakerSleepWindowInMilliseconds().get()) {
                // We push the 'circuitOpenedTime' ahead by 'sleepWindow' since we have allowed one request to try.
                // If it succeeds the circuit will be closed, otherwise another singleTest will be allowed at the end of the 'sleepWindow'.
                if (circuitOpenedOrLastTestedTime.compareAndSet(timeCircuitOpenedOrWasLastTested, System.currentTimeMillis())) {
                    // if this returns true that means we set the time so we'll return true to allow the singleTest
                    // if it returned false it means another thread raced us and allowed the singleTest before we did
                    return true;
                }
            }
            return false;
        }

邏輯很簡單,就是在一定的時間窗口內只會放行一個請求逼裆。eg.
在23:00 00.000的時間開啟了斷路器郁稍,假設斷路器的時間窗口設置為100ms。則在23:00 00.000~23:00 00.100只會允許一個請求通過胜宇。
這個主要是為了驗證依賴服務是否已經恢復正常耀怜。

三恢着、總結

這篇文章主要簡單分析了斷路器的判斷邏輯。接下來會重點分析下斷路器的數(shù)據(jù)收集的邏輯實現(xiàn)(HystrixCommandMetrics)财破。另外hystirx大量用了命令模式的實現(xiàn)(rxjava)掰派,這塊邏輯也是里面理解起來比較費力的地方。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末左痢,一起剝皮案震驚了整個濱河市靡羡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抖锥,老刑警劉巖亿眠,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異磅废,居然都是意外死亡,警方通過查閱死者的電腦和手機荆烈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門拯勉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人憔购,你說我怎么就攤上這事宫峦。” “怎么了玫鸟?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵导绷,是天一觀的道長。 經常有香客問我屎飘,道長妥曲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任钦购,我火速辦了婚禮檐盟,結果婚禮上,老公的妹妹穿的比我還像新娘押桃。我一直安慰自己葵萎,他們只是感情好,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布唱凯。 她就那樣靜靜地躺著羡忘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪磕昼。 梳的紋絲不亂的頭發(fā)上卷雕,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天,我揣著相機與錄音掰烟,去河邊找鬼爽蝴。 笑死沐批,一個胖子當著我的面吹牛,可吹牛的內容都是我干的蝎亚。 我是一名探鬼主播九孩,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼发框!你這毒婦竟也來了躺彬?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤梅惯,失蹤者是張志新(化名)和其女友劉穎宪拥,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铣减,經...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡她君,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了葫哗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缔刹。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖劣针,靈堂內的尸體忽然破棺而出校镐,到底是詐尸還是另有隱情,我是刑警寧澤捺典,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布鸟廓,位于F島的核電站,受9級特大地震影響襟己,放射性物質發(fā)生泄漏引谜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一稀蟋、第九天 我趴在偏房一處隱蔽的房頂上張望煌张。 院中可真熱鬧,春花似錦退客、人聲如沸骏融。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽档玻。三九已至,卻和暖如春茫藏,著一層夾襖步出監(jiān)牢的瞬間误趴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工务傲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凉当,地道東北人枣申。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像看杭,于是被迫代替她去往敵國和親忠藤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內容