自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

軟件系統(tǒng)過載保護(hù):熔斷器設(shè)計(jì)模式

開發(fā) 后端
在大型的軟件系統(tǒng)中,如果調(diào)用的遠(yuǎn)程服務(wù)或者資源由于某種原因無法使用時(shí),如果沒有這種過載保護(hù),就會(huì)導(dǎo)致請求的資源阻塞在服務(wù)器上等待從而耗盡系統(tǒng)或者服務(wù)器資源。很多時(shí)候剛開始可能只是系統(tǒng)出現(xiàn)了局部的、小規(guī)模的故障,然而由于種種原因,故障影響的范圍越來越大,最終導(dǎo)致了全局性的后果。

如果大家有印象的話,尤其是夏天,如果家里用電負(fù)載過大,比如開了很多家用電器,就會(huì)”自動(dòng)跳閘”,此時(shí)電路就會(huì)斷開。在以前更古老的一種方式是”保險(xiǎn)絲”,當(dāng)負(fù)載過大,或者電路發(fā)生故障或異常時(shí),電流會(huì)不斷升高,為防止升高的電流有可能損壞電路中的某些重要器件或貴重器件,燒毀電路甚至造成火災(zāi)。保險(xiǎn)絲會(huì)在電流異常升高到一定的高度和熱度的時(shí)候,自身熔斷切斷電流,從而起到保護(hù)電路安全運(yùn)行的作用。

同樣,在大型的軟件系統(tǒng)中,如果調(diào)用的遠(yuǎn)程服務(wù)或者資源由于某種原因無法使用時(shí),如果沒有這種過載保護(hù),就會(huì)導(dǎo)致請求的資源阻塞在服務(wù)器上等待從而耗盡系統(tǒng)或者服務(wù)器資源。很多時(shí)候剛開始可能只是系統(tǒng)出現(xiàn)了局部的、小規(guī)模的故障,然而由于種種原因,故障影響的范圍越來越大,最終導(dǎo)致了全局性的后果。軟件系統(tǒng)中的這種過載保護(hù)就是本文將要談到的熔斷器模式(Circuit Breaker)

一 問題的產(chǎn)生

在大型的分布式系統(tǒng)中,通常需要調(diào)用或操作遠(yuǎn)程的服務(wù)或者資源,這些遠(yuǎn)程的服務(wù)或者資源由于調(diào)用者不可以控的原因比如網(wǎng)絡(luò)連接緩慢,資源被占用或者暫時(shí)不可用等原因,導(dǎo)致對這些遠(yuǎn)程資源的調(diào)用失敗。這些錯(cuò)誤通常在稍后的一段時(shí)間內(nèi)可以恢復(fù)正常。

但是,在某些情況下,由于一些無法預(yù)知的原因?qū)е陆Y(jié)果很難預(yù)料,遠(yuǎn)程的方法或者資源可能需要很長的一段時(shí)間才能修復(fù)。這種錯(cuò)誤嚴(yán)重到系統(tǒng)的部分失去響應(yīng)甚至導(dǎo)致整個(gè)服務(wù)的完全不可用。在這種情況下,采用不斷地重試可能解決不了問題,相反,應(yīng)用程序在這個(gè)時(shí)候應(yīng)該立即返回并且報(bào)告錯(cuò)誤。

通常,如果一個(gè)服務(wù)器非常繁忙,那么系統(tǒng)中的部分失敗可能會(huì)導(dǎo)致 “連鎖失效”(cascading failure)。比如,某個(gè)操作可能會(huì)調(diào)用一個(gè)遠(yuǎn)程的WebService,這個(gè)service會(huì)設(shè)置一個(gè)超時(shí)的時(shí)間,如果響應(yīng)時(shí)間超過了該時(shí)間就會(huì)拋出一個(gè)異常。但是這種策略會(huì)導(dǎo)致并發(fā)的請求調(diào)用同樣的操作會(huì)阻塞,一直等到超時(shí)時(shí)間的到期。這種對請求的阻塞可能會(huì)占用寶貴的系統(tǒng)資源,如內(nèi)存,線程,數(shù)據(jù)庫連接等等,***這些資源就會(huì)消耗殆盡,使得其他系統(tǒng)不相關(guān)的部分所使用的資源也耗盡從而拖累整個(gè)系統(tǒng)。在這種情況下,操作立即返回錯(cuò)誤而不是等待超時(shí)的發(fā)生可能是一種更好的選擇。只有當(dāng)調(diào)用服務(wù)有可能成功時(shí)我們再去嘗試。

二 解決方法

熔斷器模式可以防止應(yīng)用程序不斷地嘗試執(zhí)行可能會(huì)失敗的操作,使得應(yīng)用程序繼續(xù)執(zhí)行而不用等待修正錯(cuò)誤,或者浪費(fèi)CPU時(shí)間去等到長時(shí)間的超時(shí)產(chǎn)生。熔斷器模式也可以使應(yīng)用程序能夠診斷錯(cuò)誤是否已經(jīng)修正,如果已經(jīng)修正,應(yīng)用程序會(huì)再次嘗試調(diào)用操作。

熔斷器模式就像是那些容易導(dǎo)致錯(cuò)誤的操作的一種代理。這種代理能夠記錄最近調(diào)用發(fā)生錯(cuò)誤的次數(shù),然后決定使用允許操作繼續(xù),或者立即返回錯(cuò)誤。

Circuit Breaker

熔斷器可以使用狀態(tài)機(jī)來實(shí)現(xiàn),內(nèi)部模擬以下幾種狀態(tài)。

  • 閉合(closed)狀態(tài): 對應(yīng)用程序的請求能夠直接引起方法的調(diào)用。代理類維護(hù)了最近調(diào)用失敗的次數(shù),如果某次調(diào)用失敗,則使失敗次數(shù)加1。如果最近失敗次數(shù)超過了在給定時(shí)間內(nèi)允許失敗的閾值,則代理類切換到斷開(Open)狀態(tài)。此時(shí)代理開啟了一個(gè)超時(shí)時(shí)鐘,當(dāng)該時(shí)鐘超過了該時(shí)間,則切換到半斷開(Half-Open)狀態(tài)。該超時(shí)時(shí)間的設(shè)定是給了系統(tǒng)一次機(jī)會(huì)來修正導(dǎo)致調(diào)用失敗的錯(cuò)誤。
  • 斷開(Open)狀態(tài):在該狀態(tài)下,對應(yīng)用程序的請求會(huì)立即返回錯(cuò)誤響應(yīng)。
  • 半斷開(Half-Open)狀態(tài):允許對應(yīng)用程序的一定數(shù)量的請求可以去調(diào)用服務(wù)。如果這些請求對服務(wù)的調(diào)用成功,那么可以認(rèn)為之前導(dǎo)致調(diào)用失敗的錯(cuò)誤已經(jīng)修正,此時(shí)熔斷器切換到閉合狀態(tài)(并且將錯(cuò)誤計(jì)數(shù)器重置);如果這一定數(shù)量的請求有調(diào)用失敗的情況,則認(rèn)為導(dǎo)致之前調(diào)用失敗的問題仍然存在,熔斷器切回到斷開方式,然后開始重置計(jì)時(shí)器來給系統(tǒng)一定的時(shí)間來修正錯(cuò)誤。半斷開狀態(tài)能夠有效防止正在恢復(fù)中的服務(wù)被突然而來的大量請求再次拖垮。

各個(gè)狀態(tài)之間的轉(zhuǎn)換如下圖:

Circuit Breaker State Change

在Close狀態(tài)下,錯(cuò)誤計(jì)數(shù)器是基于時(shí)間的。在特定的時(shí)間間隔內(nèi)會(huì)自動(dòng)重置。這能夠防止由于某次的偶然錯(cuò)誤導(dǎo)致熔斷器進(jìn)入斷開狀態(tài)。觸發(fā)熔斷器進(jìn)入斷開狀態(tài)的失敗閾值只有在特定的時(shí)間間隔內(nèi),錯(cuò)誤次數(shù)達(dá)到指定錯(cuò)誤次數(shù)的閾值才會(huì)產(chǎn)生。在Half-Open狀態(tài)中使用的連續(xù)成功次數(shù)計(jì)數(shù)器記錄調(diào)用的成功次數(shù)。當(dāng)連續(xù)調(diào)用成功次數(shù)達(dá)到某個(gè)指定值時(shí),切換到閉合狀態(tài),如果某次調(diào)用失敗,立即切換到斷開狀態(tài),連續(xù)成功調(diào)用次數(shù)計(jì)時(shí)器在下次進(jìn)入半斷開狀態(tài)時(shí)歸零。

實(shí)現(xiàn)熔斷器模式使得系統(tǒng)更加穩(wěn)定和有彈性,在系統(tǒng)從錯(cuò)誤中恢復(fù)的時(shí)候提供穩(wěn)定性,并且減少了錯(cuò)誤對系統(tǒng)性能的影像。它通過快速的拒絕那些試圖有可能調(diào)用會(huì)導(dǎo)致錯(cuò)誤的服務(wù),而不會(huì)去等待操作超時(shí)或者永遠(yuǎn)不會(huì)不返回結(jié)果來提高系統(tǒng)的響應(yīng)事件。如果熔斷器設(shè)計(jì)模式在每次狀態(tài)切換的時(shí)候會(huì)發(fā)出一個(gè)事件,這種信息可以用來監(jiān)控服務(wù)的運(yùn)行狀態(tài),能夠通知管理員在熔斷器切換到斷開狀態(tài)時(shí)進(jìn)行處理。

可以對熔斷器模式進(jìn)行定制以適應(yīng)一些可能會(huì)導(dǎo)致遠(yuǎn)程服務(wù)失敗的特定場景。比如,可以在熔斷器中對超時(shí)時(shí)間使用不斷增長的策略。在熔斷器開始進(jìn)入斷開狀態(tài)的時(shí)候,可以設(shè)置超時(shí)時(shí)間為幾秒鐘,然后如果錯(cuò)誤沒有被解決,然后將該超時(shí)時(shí)間設(shè)置為幾分鐘,依次類推。在一些情況下,在斷開狀態(tài)下我們可以返回一些錯(cuò)誤的默認(rèn)值,而不是拋出異常。

#p#

三 要考慮的因素

在實(shí)現(xiàn)熔斷器模式的時(shí)候,以下這些因素需可能需要考慮:

  • 異常處理:調(diào)用受熔斷器保護(hù)的服務(wù)的時(shí)候,我們必須要處理當(dāng)服務(wù)不可用時(shí)的異常情況。這些異常處理通常需要視具體的業(yè)務(wù)情況而定。比如,如果應(yīng)用程序只是暫時(shí)的功能降級,可能需要切換到其它的可替換的服務(wù)上來執(zhí)行相同的任務(wù)或者獲取相同的數(shù)據(jù),或者給用戶報(bào)告錯(cuò)誤然后提示他們稍后重試。
  • 異常的類型:請求失敗的原因可能有很多種。一些原因可能會(huì)比其它原因更嚴(yán)重。比如,請求會(huì)失敗可能是由于遠(yuǎn)程的服務(wù)崩潰,這可能需要花費(fèi)數(shù)分鐘來恢復(fù);也可能是由于服務(wù)器暫時(shí)負(fù)載過重導(dǎo)致超時(shí)。熔斷器應(yīng)該能夠檢查錯(cuò)誤的類型,從而根據(jù)具體的錯(cuò)誤情況來調(diào)整策略。比如,可能需要很多次超時(shí)異常才可以斷定需要切換到斷開狀態(tài),而只需要幾次錯(cuò)誤提示就可以判斷服務(wù)不可用而快速切換到斷開狀態(tài)。
  • 日志:熔斷器應(yīng)該能夠記錄所有失敗的請求,以及一些可能會(huì)嘗試成功的請求,使得的管理員能夠監(jiān)控使用熔斷器保護(hù)的服務(wù)的執(zhí)行情況。
  • 測試服務(wù)是否可用:在斷開狀態(tài)下,熔斷器可以采用定期的ping遠(yuǎn)程的服務(wù)或者資源,來判斷是否服務(wù)是否恢復(fù),而不是使用計(jì)時(shí)器來自動(dòng)切換到半斷開狀態(tài)。這種ping操作可以模擬之前那些失敗的請求,或者可以使用通過調(diào)用遠(yuǎn)程服務(wù)提供的檢查服務(wù)是否可用的方法來判斷。
  • 手動(dòng)重置:在系統(tǒng)中對于失敗操作的恢復(fù)時(shí)間是很難確定的,提供一個(gè)手動(dòng)重置功能能夠使得管理員可以手動(dòng)的強(qiáng)制將熔斷器切換到閉合狀態(tài)。同樣的,如果受熔斷器保護(hù)的服務(wù)暫時(shí)不可用的話,管理員能夠強(qiáng)制的將熔斷器設(shè)置為斷開狀態(tài)。
  • 并發(fā)問題:相同的熔斷器有可能被大量并發(fā)請求同時(shí)訪問。熔斷器的實(shí)現(xiàn)不應(yīng)該阻塞并發(fā)的請求或者增加每次請求調(diào)用的負(fù)擔(dān)。
  • 資源的差異性:使用單個(gè)熔斷器時(shí),一個(gè)資源如果​​有分布在多個(gè)地方就需要小心。比如,一個(gè)數(shù)據(jù)可能存儲在多個(gè)磁盤分區(qū)上(shard),某個(gè)分區(qū)可以正常訪問,而另一個(gè)可能存在暫時(shí)性的問題。在這種情況下,不同的錯(cuò)誤響應(yīng)如果混為一談,那么應(yīng)用程序訪問的這些存在問題的分區(qū)的失敗的可能性就會(huì)高,而那些被認(rèn)為是正常的分區(qū),就有可能被阻塞。
  • 加快熔斷器的熔斷操作:有時(shí)候,服務(wù)返回的錯(cuò)誤信息足夠讓熔斷器立即執(zhí)行熔斷操作并且保持一段時(shí)間。比如,如果從一個(gè)分布式資源返回的響應(yīng)提示負(fù)載超重,那么可以斷定出不建議立即重試,而是應(yīng)該等待幾分鐘后再重試。(HTTP協(xié)議定義了”HTTP 503 Service Unavailable”來表示請求的服務(wù)當(dāng)前不可用,他可以包含其他信息比如,超時(shí)等)
  • 重復(fù)失敗請求:當(dāng)熔斷器在斷開狀態(tài)的時(shí)候,熔斷器可以記錄每一次請求的細(xì)節(jié),而不是僅僅返回失敗信息,這樣當(dāng)遠(yuǎn)程服務(wù)恢復(fù)的時(shí)候,可以將這些失敗的請求再重新請求一次。

四 使用場景

應(yīng)該使用該模式來:

  • 防止應(yīng)用程序直接調(diào)用那些很可能會(huì)調(diào)用失敗的遠(yuǎn)程服務(wù)或共享資源。

不適合的場景

  • 對于應(yīng)用程序中的直接訪問本地私有資源,比如內(nèi)存中的數(shù)據(jù)結(jié)構(gòu),如果使用熔斷器模式只會(huì)增加系統(tǒng)額外開銷。
  • 不適合作為應(yīng)用程序中業(yè)務(wù)邏輯的異常處理替代品

五 實(shí)現(xiàn)

根據(jù)上面的狀態(tài)切換圖,我們很容易實(shí)現(xiàn)一個(gè)基本的熔斷器,只需要在內(nèi)部維護(hù)一個(gè)狀態(tài)機(jī),并定義好狀態(tài)轉(zhuǎn)移的規(guī)則,可以使用State模式來實(shí)現(xiàn)。首先,我們定義一個(gè)表示狀態(tài)轉(zhuǎn)移操作的抽象類CircuitBreakerState:

  1. public abstract class CircuitBreakerState  
  2. {  
  3.     protected CircuitBreakerState(CircuitBreaker circuitBreaker)  
  4.     {  
  5.         this.circuitBreaker = circuitBreaker;  
  6.     }  
  7.  
  8.     /// <summary>  
  9.     /// 調(diào)用受保護(hù)方法之前處理的操作  
  10.     /// </summary>  
  11.     public virtual void ProtectedCodeIsAboutToBeCalled() {  
  12.         //如果是斷開狀態(tài),直接返回  
  13.         //然后坐等超時(shí)轉(zhuǎn)換到半斷開狀態(tài)  
  14.         if (circuitBreaker.IsOpen)  
  15.         {  
  16.             throw new OpenCircuitException();  
  17.         }  
  18.     }  
  19.  
  20.     /// <summary>  
  21.     /// 受熔斷器保護(hù)的方法調(diào)用成功之后的操作  
  22.     /// </summary>  
  23.     public virtual void ProtectedCodeHasBeenCalled()  
  24.     {  
  25.         circuitBreaker.IncreaseSuccessCount();  
  26.     }  
  27.  
  28.     /// <summary>  
  29.     ///受熔斷器保護(hù)的方法調(diào)用發(fā)生異常操作后的操作  
  30.     /// </summary>  
  31.     /// <param name="e"></param>  
  32.     public virtual void ActUponException(Exception e)  
  33.     {  
  34.         //增加失敗次數(shù)計(jì)數(shù)器,并且保存錯(cuò)誤信息  
  35.         circuitBreaker.IncreaseFailureCount(e);  
  36.         //重置連續(xù)成功次數(shù)  
  37.         circuitBreaker.ResetConsecutiveSuccessCount();  
  38.     }  
  39.  
  40.     protected readonly CircuitBreaker circuitBreaker;  

抽象類中,狀態(tài)機(jī)CircuitBreaker通過構(gòu)造函數(shù)注入;當(dāng)發(fā)生錯(cuò)誤時(shí),我們增加錯(cuò)誤計(jì)數(shù)器,并且重置連續(xù)成功計(jì)數(shù)器,在增加錯(cuò)誤計(jì)數(shù)器操作中,同時(shí)也記錄了出錯(cuò)的異常信息。

然后在分別實(shí)現(xiàn)表示熔斷器三個(gè)狀態(tài)的類。首先實(shí)現(xiàn)閉合狀態(tài)CloseState:

  1. public class ClosedState : CircuitBreakerState  
  2. {  
  3.     public ClosedState(CircuitBreaker circuitBreaker)  
  4.         : base(circuitBreaker)  
  5.     {  
  6.         //重置失敗計(jì)數(shù)器  
  7.         circuitBreaker.ResetFailureCount();  
  8.     }  
  9.  
  10.     public override void ActUponException(Exception e)  
  11.     {  
  12.         base.ActUponException(e);  
  13.         //如果失敗次數(shù)達(dá)到閾值,則切換到斷開狀態(tài)  
  14.         if (circuitBreaker.FailureThresholdReached())  
  15.         {  
  16.             circuitBreaker.MoveToOpenState();  
  17.         }  
  18.     }  

在閉合狀態(tài)下,如果發(fā)生錯(cuò)誤,并且錯(cuò)誤次數(shù)達(dá)到閾值,則狀態(tài)機(jī)切換到斷開狀態(tài)。斷開狀態(tài)OpenState的實(shí)現(xiàn)如下:

  1. public class OpenState : CircuitBreakerState  
  2. {  
  3.     private readonly Timer timer;  
  4.  
  5.     public OpenState(CircuitBreaker circuitBreaker)  
  6.         : base(circuitBreaker)  
  7.     {  
  8.         timer = new Timer(circuitBreaker.Timeout.TotalMilliseconds);  
  9.         timer.Elapsed += TimeoutHasBeenReached;  
  10.         timer.AutoReset = false;  
  11.         timer.Start();  
  12.     }  
  13.  
  14.     //斷開超過設(shè)定的閾值,自動(dòng)切換到半斷開狀態(tài)  
  15.     private void TimeoutHasBeenReached(object sender, ElapsedEventArgs e)  
  16.     {  
  17.         circuitBreaker.MoveToHalfOpenState();  
  18.     }  
  19.  
  20.     public override void ProtectedCodeIsAboutToBeCalled()  
  21.     {  
  22.         base.ProtectedCodeIsAboutToBeCalled();  
  23.         throw new OpenCircuitException();  
  24.     }  

斷開狀態(tài)內(nèi)部維護(hù)一個(gè)計(jì)數(shù)器,如果斷開達(dá)到一定的時(shí)間,則自動(dòng)切換到版斷開狀態(tài),并且,在斷開狀態(tài)下,如果需要執(zhí)行操作,則直接拋出異常。

#p#

***半斷開Half-Open狀態(tài)實(shí)現(xiàn)如下:

  1. public class HalfOpenState : CircuitBreakerState  
  2. {  
  3.     public HalfOpenState(CircuitBreaker circuitBreaker)  
  4.         : base(circuitBreaker)  
  5.     {  
  6.         //重置連續(xù)成功計(jì)數(shù)  
  7.         circuitBreaker.ResetConsecutiveSuccessCount();  
  8.     }  
  9.  
  10.     public override void ActUponException(Exception e)  
  11.     {  
  12.         base.ActUponException(e);  
  13.         //只要有失敗,立即切換到斷開模式  
  14.         circuitBreaker.MoveToOpenState();  
  15.     }  
  16.  
  17.     public override void ProtectedCodeHasBeenCalled()  
  18.     {  
  19.         base.ProtectedCodeHasBeenCalled();  
  20.         //如果連續(xù)成功次數(shù)達(dá)到閾值,切換到閉合狀態(tài)  
  21.         if (circuitBreaker.ConsecutiveSuccessThresholdReached())  
  22.         {  
  23.             circuitBreaker.MoveToClosedState();  
  24.         }  
  25.     }  

切換到半斷開狀態(tài)時(shí),將連續(xù)成功調(diào)用計(jì)數(shù)重置為0,當(dāng)執(zhí)行成功的時(shí)候,自增改字段,當(dāng)達(dá)到連讀調(diào)用成功次數(shù)的閾值時(shí),切換到閉合狀態(tài)。如果調(diào)用失敗,立即切換到斷開模式。

有了以上三種狀態(tài)切換之后,我們要實(shí)現(xiàn)CircuitBreaker類了:

  1. public class CircuitBreaker  
  2. {  
  3.     private readonly object monitor = new object();  
  4.     private CircuitBreakerState state;  
  5.     public int FailureCount { get; private set; }  
  6.     public int ConsecutiveSuccessCount { get; private set; }  
  7.     public int FailureThreshold { get; private set; }  
  8.     public int ConsecutiveSuccessThreshold { get; private set; }  
  9.     public TimeSpan Timeout { get; private set; }  
  10.     public Exception LastException { get; private set; }  
  11.  
  12.     public bool IsClosed  
  13.     {  
  14.         get { return state is ClosedState; }  
  15.     }  
  16.  
  17.     public bool IsOpen  
  18.     {  
  19.         get { return state is OpenState; }  
  20.     }  
  21.  
  22.     public bool IsHalfOpen  
  23.     {  
  24.         get { return state is HalfOpenState; }  
  25.     }  
  26.  
  27.     internal void MoveToClosedState()  
  28.     {  
  29.         state = new ClosedState(this);  
  30.     }  
  31.  
  32.     internal void MoveToOpenState()  
  33.     {  
  34.         state = new OpenState(this);  
  35.     }  
  36.  
  37.     internal void MoveToHalfOpenState()  
  38.     {  
  39.         state = new HalfOpenState(this);  
  40.     }  
  41.  
  42.     internal void IncreaseFailureCount(Exception ex)  
  43.     {  
  44.         LastException = ex;  
  45.         FailureCount++;  
  46.     }  
  47.  
  48.     internal void ResetFailureCount()  
  49.     {  
  50.         FailureCount = 0;  
  51.     }  
  52.  
  53.     internal bool FailureThresholdReached()  
  54.     {  
  55.         return FailureCount >= FailureThreshold;  
  56.     }  
  57.  
  58.     internal void IncreaseSuccessCount()  
  59.     {  
  60.         ConsecutiveSuccessCount++;  
  61.     }  
  62.  
  63.     internal void ResetConsecutiveSuccessCount()  
  64.     {  
  65.         ConsecutiveSuccessCount = 0;  
  66.     }  
  67.  
  68.     internal bool ConsecutiveSuccessThresholdReached()  
  69.     {  
  70.         return ConsecutiveSuccessCount >= ConsecutiveSuccessThreshold;  
  71.     }  
  72.  

在該類中首先:

  • 定義了一些記錄狀態(tài)的變量,如FailureCount,ConsecutiveSuccessCount 記錄失敗次數(shù),連續(xù)成功次數(shù),以及FailureThreshold,ConsecutiveSuccessThreshold記錄***調(diào)用失敗次數(shù),連續(xù)調(diào)用成功次數(shù)。這些對象對外部來說是只讀的。
  • 定義了一個(gè) CircuitBreakerState類型的state變量,以表示當(dāng)前系統(tǒng)的狀態(tài)。
  • 定義了一些列獲取當(dāng)前狀態(tài)的方法IsOpen,IsClose,IsHalfOpen,以及表示狀態(tài)轉(zhuǎn)移的方法MoveToOpenState,MoveToClosedState等,這些方法比較簡單,根據(jù)名字即可看出用意。

然后,可以通過構(gòu)造函數(shù)將在Close狀態(tài)下***失敗次數(shù),HalfOpen狀態(tài)下使用的***連續(xù)成功次數(shù),以及Open狀態(tài)下的超時(shí)時(shí)間通過構(gòu)造函數(shù)傳進(jìn)來:

  1. public CircuitBreaker(int failedthreshold, int consecutiveSuccessThreshold, TimeSpan timeout)  
  2. {  
  3.     if (failedthreshold < 1 || consecutiveSuccessThreshold < 1)  
  4.     {  
  5.         throw new ArgumentOutOfRangeException("threshold""Threshold should be greater than 0");  
  6.     }  
  7.  
  8.     if (timeout.TotalMilliseconds < 1)  
  9.     {  
  10.         throw new ArgumentOutOfRangeException("timeout""Timeout should be greater than 0");  
  11.     }  
  12.  
  13.     FailureThreshold = failedthreshold;  
  14.     ConsecutiveSuccessThreshold = consecutiveSuccessThreshold;  
  15.     Timeout = timeout;  
  16.     MoveToClosedState();  

在初始狀態(tài)下,熔斷器切換到閉合狀態(tài)。

然后,可以通過AttempCall調(diào)用,傳入期望執(zhí)行的代理方法,該方法的執(zhí)行受熔斷器保護(hù)。這里使用了鎖來處理并發(fā)問題。

  1. public void AttemptCall(Action protectedCode)  
  2. {  
  3.     using (TimedLock.Lock(monitor))  
  4.     {  
  5.         state.ProtectedCodeIsAboutToBeCalled();  
  6.     }  
  7.  
  8.     try 
  9.     {  
  10.         protectedCode();  
  11.     }  
  12.     catch (Exception e)  
  13.     {  
  14.         using (TimedLock.Lock(monitor))  
  15.         {  
  16.             state.ActUponException(e);  
  17.         }  
  18.         throw;  
  19.     }  
  20.  
  21.     using (TimedLock.Lock(monitor))  
  22.     {  
  23.         state.ProtectedCodeHasBeenCalled();  
  24.     }  

***,提供Close和Open兩個(gè)方法來手動(dòng)切換當(dāng)前狀態(tài)。

  1. public void Close()  
  2. {  
  3.     using (TimedLock.Lock(monitor))  
  4.     {  
  5.         MoveToClosedState();  
  6.     }  
  7. }  
  8.  
  9. public void Open()  
  10. {  
  11.     using (TimedLock.Lock(monitor))  
  12.     {  
  13.         MoveToOpenState();  
  14.     }  

#p#

六 測試

以上的熔斷模式,我們可以對其建立單元測試。

首先我們編寫幾個(gè)幫助類以模擬連續(xù)執(zhí)行次數(shù):

  1. private static void CallXAmountOfTimes(Action codeToCall, int timesToCall)  
  2. {  
  3.     for (int i = 0; i < timesToCall; i++)  
  4.     {  
  5.         codeToCall();  
  6.     }  

以下類用來拋出特定異常:

  1. private static void AssertThatExceptionIsThrown<T>(Action code) where T : Exception  
  2. {  
  3.     try 
  4.     {  
  5.         code();  
  6.     }  
  7.     catch (T)  
  8.     {  
  9.         return;  
  10.     }  
  11.  
  12.     Assert.Fail("Expected exception of type {0} was not thrown", typeof(T).FullName);  

然后,使用NUnit,可以建立如下Case:

  1. [Test]  
  2. public void ClosesIfProtectedCodeSucceedsInHalfOpenState()  
  3. {  
  4.     var stub = new Stub(10);  
  5.     //定義熔斷器,失敗10次進(jìn)入斷開狀態(tài)  
  6.     //5秒后進(jìn)入半斷開狀態(tài)  
  7.     //在半斷開狀態(tài)下,連續(xù)成功15次,進(jìn)入閉合狀態(tài)  
  8.     var circuitBreaker = new CircuitBreaker(1015, TimeSpan.FromMilliseconds(5000));  
  9.     Assert.That(circuitBreaker.IsClosed);  
  10.     //失敗10次調(diào)用  
  11.     CallXAmountOfTimes(() => AssertThatExceptionIsThrown<ApplicationException>(() => circuitBreaker.AttemptCall(stub.DoStuff)), 10);  
  12.  
  13.     Assert.AreEqual(10, circuitBreaker.FailureCount);  
  14.  
  15.     Assert.That(circuitBreaker.IsOpen);  
  16.  
  17.     //等待從Open轉(zhuǎn)到HalfOpen  
  18.     Thread.Sleep(6000);  
  19.     Assert.That(circuitBreaker.IsHalfOpen);  
  20.     //成功調(diào)用15次  
  21.     CallXAmountOfTimes(()=>circuitBreaker.AttemptCall(stub.DoStuff), 15);  
  22.  
  23.     Assert.AreEqual(15, circuitBreaker.ConsecutiveSuccessCount);  
  24.     Assert.AreEqual(0, circuitBreaker.FailureCount);  
  25.     Assert.That(circuitBreaker.IsClosed);  

這個(gè)Case模擬了熔斷器中狀態(tài)的轉(zhuǎn)換。首先初始化時(shí),熔斷器處于閉合狀態(tài),然后連續(xù)10次調(diào)用拋出異常,這時(shí)熔斷器進(jìn)去了斷開狀態(tài),然后讓線程等待6秒,此時(shí)在第5秒的時(shí)候,狀態(tài)切換到了半斷開狀態(tài)。然后連續(xù)15次成功調(diào)用,此時(shí)狀態(tài)又切換到了閉合狀態(tài)。

七 結(jié)論

在應(yīng)用系統(tǒng)中,我們通常會(huì)去調(diào)用遠(yuǎn)程的服務(wù)或者資源(這些服務(wù)或資源通常是來自第三方),對這些遠(yuǎn)程服務(wù)或者資源的調(diào)用通常會(huì)導(dǎo)致失敗,或者掛起沒有響應(yīng),直到超時(shí)的產(chǎn)生。在一些極端情況下,大量的請求會(huì)阻塞在對這些異常的遠(yuǎn)程服務(wù)的調(diào)用上,會(huì)導(dǎo)致一些關(guān)鍵性的系統(tǒng)資源耗盡,從而導(dǎo)致級聯(lián)的失敗,從而拖垮整個(gè)系統(tǒng)。熔斷器模式在內(nèi)部采用狀態(tài)機(jī)的形式,使得對這些可能會(huì)導(dǎo)致請求失敗的遠(yuǎn)程服務(wù)進(jìn)行了包裝,當(dāng)遠(yuǎn)程服務(wù)發(fā)生異常時(shí),可以立即對進(jìn)來的請求返回錯(cuò)誤響應(yīng),并告知系統(tǒng)管理員,將錯(cuò)誤控制在局部范圍內(nèi),從而提高系統(tǒng)的穩(wěn)定性和可靠性。

本文首先介紹了熔斷器模式使用的場景,能夠解決的問題,以及需要考慮的因素,***使用代碼展示了如何實(shí)現(xiàn)一個(gè)簡單的熔斷器,并且給出了測試用例,希望這些對您有幫助,尤其是在當(dāng)您的系統(tǒng)調(diào)用了外部的遠(yuǎn)程服務(wù)或者資源,同時(shí)訪問量又很大的情況下對提高系統(tǒng)的穩(wěn)定性和可靠性有所幫助。

八 參考文獻(xiàn)

1. 互聯(lián)網(wǎng)巨頭為什么會(huì)“宕機(jī)”, http://edge.iteye.com/blog/1933145

2. 互聯(lián)網(wǎng)巨頭為什么會(huì)“宕機(jī)”(二), http://edge.iteye.com/blog/1936151

3. Circuit Breaker, http://martinfowler.com/bliki/CircuitBreaker.html

4. Circuit Breaker Pattern, http://msdn.microsoft.com/en-us/library/dn589784.aspx

原文鏈接:http://www.cnblogs.com/yangecnu/p/Introduce-Circuit-Breaker-Pattern.html

責(zé)任編輯:林師授 來源: yangecnu's Blog
相關(guān)推薦

2021-11-25 09:55:47

Golang熔斷器語言

2022-05-13 09:05:49

Hystrix熔斷器

2018-12-14 08:52:38

過載保護(hù)異構(gòu)服務(wù)器負(fù)載均衡

2023-08-28 08:00:45

2012-06-26 10:03:06

海量數(shù)據(jù)處理

2024-03-19 08:01:54

服務(wù)熔斷軟件設(shè)計(jì)模式微服務(wù)

2016-09-21 13:52:53

服務(wù)器負(fù)載過載保護(hù)

2024-10-25 08:11:37

2019-08-12 14:45:50

軟件設(shè)計(jì)Java

2023-05-15 08:51:46

解釋器模式定義

2020-11-09 08:20:33

解釋器模式

2023-09-04 13:14:00

裝飾器設(shè)計(jì)模式

2010-08-11 09:15:07

設(shè)計(jì)模式Python

2012-05-08 14:39:13

虛擬系統(tǒng)

2023-12-13 13:28:16

裝飾器模式Python設(shè)計(jì)模式

2021-06-22 15:27:13

設(shè)計(jì)模式迭代器模式Java

2012-05-17 15:02:23

騰訊技術(shù)

2025-04-03 10:04:53

服務(wù)降級分布式系統(tǒng)系統(tǒng)

2010-04-21 08:38:18

解釋器模式PHP設(shè)計(jì)模式

2022-01-19 08:21:12

設(shè)計(jì)裝飾器模式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號