CircuitBreaker 斷路器的基本概念和原理

最近在接 spring cloug hystrix 熔斷叭爱,了解一下熔斷的基本概念和原理座每。此篇原文是馬丁花的文章 https://martinfowler.com/bliki/CircuitBreaker.html 覺得不錯杨幼,一邊閱讀一邊翻譯過來以加深理解,語文不好請見諒虑啤。

軟件系統(tǒng)向其它運行于不同進程拂募,或是網(wǎng)絡(luò)中不同機器上的軟件發(fā)起遠程調(diào)用是很常見的。內(nèi)存調(diào)用和遠程調(diào)用的一個比較大的差異是帅刊,遠程調(diào)用可能失敗或者掛起直到某一超時時限纸泡。而對于一個不能響應(yīng)的服務(wù)提供方,如果有很多調(diào)用方依賴于它赖瞒,情況會更糟糕女揭,因為你將會耗盡關(guān)鍵資源蚤假,然后引發(fā)多個系統(tǒng)的級聯(lián)奔潰。在作者的神書 《Release It》中吧兔, Michael Nygard 推廣 Circuit Breaker 模式來防止這種可怕的級聯(lián)影響磷仰。

斷路器的基本思路很簡單。通過將待保護的函數(shù)調(diào)用包裹在斷路器對象中境蔼,讓斷路器對象來監(jiān)控失敗灶平。當失敗次數(shù)達到特定的閾值時,斷路器打開欧穴,后續(xù)對此斷路器對象的訪問將直接返回 error民逼,根本不會調(diào)用受保護的函數(shù)泵殴。通常涮帘,你會想在斷路器打開的時候得到某種監(jiān)控預(yù)警。


斷路器執(zhí)行過程

這邊是個 ruby 寫的描述斷路器這種行為(對超時調(diào)用的保護)的簡單示例笑诅。(雖然是 ruby 的调缨,但是基本代碼邏輯還是可以看懂的)

首先新建一個斷路器,傳入需保護的函數(shù)調(diào)用吆你,這是個 lambda 表達式
cb = CircuitBreaker.new {|arg| @supplier.func arg}

斷路器保存函數(shù)塊弦叶,初始化一些參數(shù)(閾值、超時時間和監(jiān)控)妇多,并重置斷路器為關(guān)閉狀態(tài)伤哺。

class CircuitBreaker

  attr_accessor :invocation_timeout, :failure_threshold, :monitor
  def initialize &block
    @circuit = block
    @invocation_timeout = 0.01
    @failure_threshold = 5
    @monitor = acquire_monitor
    reset
  end

如果斷路器處于關(guān)閉狀態(tài),調(diào)用會通過斷路器訪問內(nèi)部的函數(shù)者祖;如果處于開啟狀態(tài)的話立莉,則會直接返回錯誤。

# client code
    aCircuitBreaker.call(5)

class CircuitBreaker...
  def call args
    case state
    when :closed
      begin
        do_call args              // 斷路器關(guān)閉時七问,調(diào)用內(nèi)部函數(shù)
      rescue Timeout::Error
        record_failure
        raise $!
      end
    when :open then raise CircuitBreaker::Open  // 斷路器開啟蜓耻,直接拋出異常 
    else raise "Unreachable Code"
    end
  end
  def do_call args
    result = Timeout::timeout(@invocation_timeout) do
      @circuit.call args
    end
    reset
    return result
  end

當調(diào)用超時時,遞增失敗計數(shù)器械巡;調(diào)用成功的話刹淌,再把它重置為0。

class CircuitBreaker...

  def record_failure
    @failure_count += 1
    @monitor.alert(:open_circuit) if :open == state
  end
  def reset
    @failure_count = 0
    @monitor.alert :reset_circuit
  end

斷路器的狀態(tài)由一個閾值和失敗次數(shù)的對比決定

class CircuitBreaker...

  def state
     (@failure_count >= @failure_threshold) ? :open : :closed
  end

這個簡易的斷路器在開啟時可以避免對保護函數(shù)的調(diào)用讥耗,不過在系統(tǒng)恢復(fù)的時候需要額外的干預(yù)來重置斷路器有勾。在建筑物中的電路斷路器是合理的,不過對于軟件斷路器古程,我們可以讓斷路器自身檢測內(nèi)部調(diào)用是否恢復(fù)蔼卡。為了實現(xiàn)這個,我們可以間隔一個合適時間段后嘗試調(diào)用籍琳,如果調(diào)用成功菲宴,則重置斷路器贷祈。

斷路器的開啟和恢復(fù)邏輯

這種斷路器,需要增加一個閾值來重置斷路器喝峦,還需要一個變量來存儲上次失敗發(fā)生的時間

class ResetCircuitBreaker...

  def initialize &block
    @circuit = block
    @invocation_timeout = 0.01
    @failure_threshold = 5
    @monitor = BreakerMonitor.new
    @reset_timeout = 0.1
    reset
  end
  def reset   // 重置
    @failure_count = 0
    @last_failure_time = nil
    @monitor.alert :reset_circuit
  end

現(xiàn)在就出現(xiàn)了第三種狀態(tài) “半開”势誊,這時,斷路器準備好發(fā)起一個真實的調(diào)用來檢驗問題是否已經(jīng)修復(fù)谣蠢。

class ResetCircuitBreaker...

  def state
    case
// 失敗次數(shù)大于閾值(開啟) 并且開啟一段時間后粟耻,為“半開”狀態(tài), 這種狀態(tài)會去檢驗
    when (@failure_count >= @failure_threshold) && 
        (Time.now - @last_failure_time) > @reset_timeout
      :half_open
    when (@failure_count >= @failure_threshold)
      :open
    else
      :closed
    end
  end

“半開” 狀態(tài)下的調(diào)用是一個測探眉踱,調(diào)用成功就重置斷路器挤忙,否則重置超時

class ResetCircuitBreaker...

  def call args
    case state
    when :closed, :half_open
      begin
        do_call args
      rescue Timeout::Error
        record_failure
        raise $!
      end
    when :open
      raise CircuitBreaker::Open
    else
      raise "Unreachable"
    end
  end
  def record_failure
    @failure_count += 1
    @last_failure_time = Time.now   // 重置超時,重新計算下個半開的時間窗口
    @monitor.alert(:open_circuit) if :open == state
  end

這個例子用以解釋原理的谈喳,相對簡單册烈,真實的斷路器會提供更多的功能和參數(shù)。通常它們會將受保護的調(diào)用可能引發(fā)的異常(如網(wǎng)絡(luò)連接失斝銮荨)給隔離開來赏僧。并不是所有的錯誤都會造成短路,有些錯誤是反應(yīng)正常的失敗的扭倾,應(yīng)該作為正常邏輯的一部分處理淀零。

訪問量大的時候,我們的很多請求會遇到一直等待響應(yīng)超時的問題膛壹。由于遠程調(diào)用通常都比較慢驾中,將每個請求放置在不同的線程里,并使用 future or promise 的方式來處理響應(yīng)會是個好主意模聋。這些線程從線程池中獲取肩民,這樣,你就可以在線程池過載的時候?qū)嗦菲鲾嚅_撬槽。

例子展示了斷路跳閘的一種簡單方式此改,在調(diào)用成功的時候重置計數(shù)器。復(fù)雜點的方式可能會觀察錯誤頻率侄柔,比如說在 50% 失敗率的時候跳閘共啃。你也可以對不同的錯誤設(shè)置不同的閾值,比如設(shè)置超時的閾值為 10% 暂题,而將連接失敗的閾值設(shè)置為 3% 移剪。

上面斷路器的例子只是針對同步調(diào)用的,其實斷路器在異步通信時也是有用的薪者。常用的技術(shù)有纵苛,將所有請求推入到一個隊列中,服務(wù)提供方根據(jù)自身頻率來消費,有效的避免了服務(wù)器超載攻人。這時取试,斷路器則在隊列滿的時候斷開。

斷路器能減少在操作過程中容易失敗的資源捆綁在一起怀吻。你不再需要等待客戶端超時瞬浓,斷路器避免了再向壓力過大的系統(tǒng)增加負載。這邊講的遠程調(diào)用是斷路器常見的使用場景蓬坡,它們也能用于任何你想將系統(tǒng)的一些部分隔離于其他部分的失敗的場景猿棉。
監(jiān)控斷路器是很有價值的。斷路器狀態(tài)的任何變化都應(yīng)該記錄于日志中屑咳,并且披露出這些狀態(tài)的詳細細節(jié)以供更深入的監(jiān)控萨赁。斷路器的行為通常預(yù)示著更深層次的環(huán)境問題。操作員應(yīng)該能打開和重置斷路器兆龙。

斷路器本身是很有價值的杖爽,但是客戶端使用它的時候需要對斷路失敗做相應(yīng)的處理。比如详瑞,對于遠程調(diào)用掂林,你需要思考調(diào)用失敗的時候怎么處理臣缀。是你執(zhí)行的操作失敗了坝橡,或是有什么你可以補救的?信用卡授權(quán)可以放于隊列中后續(xù)再處理精置,獲取數(shù)據(jù)失敗可以通過顯示某些固定的數(shù)據(jù)计寇,這些數(shù)據(jù)足夠豐富,不會影響展示脂倦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末番宁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子赖阻,更是在濱河造成了極大的恐慌蝶押,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件火欧,死亡現(xiàn)場離奇詭異棋电,居然都是意外死亡,警方通過查閱死者的電腦和手機苇侵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門赶盔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人榆浓,你說我怎么就攤上這事于未。” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵烘浦,是天一觀的道長抖坪。 經(jīng)常有香客問我,道長闷叉,這世上最難降的妖魔是什么柳击? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮片习,結(jié)果婚禮上捌肴,老公的妹妹穿的比我還像新娘。我一直安慰自己藕咏,他們只是感情好状知,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著孽查,像睡著了一般饥悴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盲再,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天西设,我揣著相機與錄音,去河邊找鬼答朋。 笑死贷揽,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的梦碗。 我是一名探鬼主播禽绪,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼洪规!你這毒婦竟也來了印屁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤斩例,失蹤者是張志新(化名)和其女友劉穎雄人,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體念赶,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡础钠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了晶乔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片珍坊。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖正罢,靈堂內(nèi)的尸體忽然破棺而出阵漏,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布履怯,位于F島的核電站回还,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏叹洲。R本人自食惡果不足惜柠硕,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望运提。 院中可真熱鬧蝗柔,春花似錦、人聲如沸民泵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽栈妆。三九已至胁编,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鳞尔,已是汗流浹背嬉橙。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留寥假,地道東北人市框。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像昧旨,于是被迫代替她去往敵國和親拾给。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354

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