鍍金池/ 教程/ 大數(shù)據(jù)/ 斷路器模式
索引表模式
Sharding 分片模式
外部配置存儲(chǔ)模式
命令和查詢職責(zé)分離(CQRS)模式
靜態(tài)內(nèi)容托管模式
運(yùn)行重構(gòu)模式
計(jì)算資源整合模式
Throttling 節(jié)流模式
斷路器模式
事件獲取模式
實(shí)體化視圖模式
緩存預(yù)留模式
守門員模式
聯(lián)合身份模式
補(bǔ)償交易模式
重試模式
領(lǐng)導(dǎo)人選舉模式
優(yōu)先級(jí)隊(duì)列模式
健康端點(diǎn)監(jiān)控模式
消費(fèi)者的競(jìng)爭(zhēng)模式
基于隊(duì)列的負(fù)載均衡模式
仆人鍵模式
管道和過(guò)濾器模式
調(diào)度程序代理管理者模式

斷路器模式

處理故障連接到遠(yuǎn)程服務(wù)或資源時(shí),可能需要耗費(fèi)大量的時(shí)間。這種模式可以提高應(yīng)用程序的穩(wěn)定性和靈活性。

背景和問(wèn)題

在分布式環(huán)境中,如在云,其中,應(yīng)用程序執(zhí)行訪問(wèn)遠(yuǎn)程資源和服務(wù)的操作,有可能對(duì)這些操作的失敗是由于瞬時(shí)故障,如慢的網(wǎng)絡(luò)連接,超時(shí),或者被過(guò)度使用的資源或暫時(shí)不可用。這些故障一般之后的短時(shí)間內(nèi)糾正自己,和一個(gè)強(qiáng)大的云應(yīng)用應(yīng)該準(zhǔn)備使用的策略來(lái)處理它們,例如,通過(guò)重試模式進(jìn)行說(shuō)明。

但是,也可以是其中的故障是由于那些不容易預(yù)見(jiàn)的突發(fā)事件的情況下,這可能需要更長(zhǎng)的時(shí)間來(lái)糾正。這些故障從連接的部分損失到服務(wù)的完整的故障范圍的嚴(yán)重程度。在這種情況下,它可能是毫無(wú)意義的應(yīng)用,不斷重試執(zhí)行的操作是不太可能成功,而不是應(yīng)用程序應(yīng)該很快接受該操作已失敗,并相應(yīng)地處理這個(gè)故障。

此外,如果一個(gè)服務(wù)是非常繁忙的,在系統(tǒng)中的一個(gè)部分出現(xiàn)故障可能會(huì)導(dǎo)致連鎖故障。例如,調(diào)用一個(gè)服務(wù)的操作可被配置成實(shí)現(xiàn)一個(gè)超時(shí),如果該服務(wù)無(wú)法在這段時(shí)間內(nèi)響應(yīng)一個(gè)失敗消息答復(fù)。然而,這一策略可能導(dǎo)致許多并發(fā)請(qǐng)求的相同的操作,直到超時(shí)時(shí)段期滿被阻止。這些被禁止的請(qǐng)求可能會(huì)持有關(guān)鍵系統(tǒng)資源,如內(nèi)存,線程,數(shù)據(jù)庫(kù)連接等。因此,這些資源可能會(huì)耗盡,從而導(dǎo)致該系統(tǒng)的其他可能無(wú)關(guān)的部件,需要使用相同的資源的失敗。在這些情況下,這將是優(yōu)選的操作立即失敗,并且只嘗試調(diào)用服務(wù),如果它是可能成功。注意,設(shè)置一個(gè)較短的超時(shí)可能有助于解決此問(wèn)題,但在超時(shí)不應(yīng)該如此之短,以致操作失敗的大部分時(shí)間,即使該請(qǐng)求到服務(wù)最終會(huì)成功。

解決方案

該斷路器圖案可以防止一個(gè)應(yīng)用程序多次試圖執(zhí)行一個(gè)操作,即很可能失敗,允許它繼續(xù)而不等待故障恢復(fù)或者浪費(fèi) CPU 周期,而它確定該故障是持久的。斷路器模式也使應(yīng)用程序能夠檢測(cè)故障是否已經(jīng)解決。如果問(wèn)題似乎已經(jīng)得到糾正??,應(yīng)用程序可以嘗試調(diào)用操作。

注意

斷路器圖案的目的是從該重試模式的不同。重試模式使應(yīng)用程序可以重試操作以期望它會(huì)成功。斷路器圖案防止應(yīng)用程序執(zhí)行一個(gè)操作,即很可能失敗。一個(gè)應(yīng)用程序可以通過(guò)使用重試模式,通過(guò)一個(gè)斷路器調(diào)用操作結(jié)合這兩種模式。然而,在重試邏輯應(yīng)該是由斷路器返回任何異常敏感和放棄重試次數(shù),如果斷路器指示故障不是瞬時(shí)的。

斷路器充當(dāng)可能失敗操作的代理。代理應(yīng)監(jiān)測(cè)最近發(fā)生的故障數(shù)量,然后使用這個(gè)信息來(lái)決定是否允許該操作繼續(xù)進(jìn)行,或簡(jiǎn)單地立即返回一個(gè)異常。

代理可以被實(shí)現(xiàn)為狀態(tài)機(jī)與模擬的電路斷路器的功能如下?tīng)顟B(tài):

關(guān)閉:從應(yīng)用程序的請(qǐng)求是通過(guò)對(duì)操作進(jìn)行路由。代理保持最近的失敗次數(shù)的計(jì)數(shù),并且如果該呼叫到操作不成功,則代理遞增該計(jì)數(shù)。如果最近的失敗次數(shù)超過(guò)了一個(gè)給定時(shí)間周期內(nèi)的規(guī)定的閾值時(shí),該代理將被置于打開(kāi)狀態(tài)。在這一點(diǎn)上的代理啟動(dòng)一個(gè)超時(shí)定時(shí)器,當(dāng)該定時(shí)器期滿的代理放置到半開(kāi)放狀態(tài)。

注意

超時(shí)定時(shí)器的目的是為了給系統(tǒng)時(shí)間,糾正允許應(yīng)用程序嘗試再次執(zhí)行該操作之前導(dǎo)致失敗的問(wèn)題。

  • 打開(kāi):從應(yīng)用程序請(qǐng)求立即失敗和異常返回給應(yīng)用程序。
  • 半開(kāi)放:從應(yīng)用程序請(qǐng)求的數(shù)量有限允許通過(guò)并調(diào)用運(yùn)行。如果這些請(qǐng)求是成功的,則假定先前導(dǎo)致故障的故障已修復(fù)和斷路器切換到閉合狀態(tài)(故障計(jì)數(shù)器被復(fù)位)。如果任何請(qǐng)求失敗,斷路器假設(shè)故障仍然存在,因此恢復(fù)到打開(kāi)狀態(tài),并重新啟動(dòng)超時(shí)定時(shí)器,系統(tǒng)的時(shí)間再延長(zhǎng),從故障中恢復(fù)。

注意

半開(kāi)的狀態(tài)是很有用的,以防止恢復(fù)服務(wù),從突然被淹沒(méi)的請(qǐng)求。作為服務(wù)恢復(fù),也可能是能夠支持請(qǐng)求的限制音量,直到恢復(fù)完成,但在恢復(fù)過(guò)程中,海量的工作可能會(huì)導(dǎo)致服務(wù)超時(shí)或再次失敗。

下圖展示出了用于一個(gè)可能的實(shí)現(xiàn)的電路斷路器的狀態(tài)。

http://wiki.jikexueyuan.com/project/cloud-design-patterns/images/cbm.png" alt="" /> 圖 1 - 斷路器狀態(tài)

需要注意的是,在圖 1 中,所用的封閉狀態(tài)下的失敗計(jì)數(shù)器是基于時(shí)間的。它以定期自動(dòng)復(fù)位。這有助于防止斷路器進(jìn)入打開(kāi)狀態(tài),如果它經(jīng)受偶然的失敗;這使斷路器跳閘進(jìn)入打開(kāi)狀態(tài)的故障閾值時(shí),故障的指定數(shù)量的指定的時(shí)間間隔期間發(fā)生的僅達(dá)到。所使用的半開(kāi)狀態(tài)下的成功計(jì)數(shù)器記錄成功嘗試調(diào)用的操作的數(shù)量。斷路器恢復(fù)到封閉狀態(tài)后的連續(xù)操作調(diào)用中指定數(shù)量的已成功。如果任何調(diào)用失敗時(shí),斷路器立即進(jìn)入打開(kāi)狀態(tài),并且成功的計(jì)數(shù)器將其進(jìn)入半開(kāi)狀態(tài)下一次復(fù)位。

Note

如何將系統(tǒng)恢復(fù)從外部處理,可能通過(guò)恢復(fù)或重新啟動(dòng)故障部件或修理的網(wǎng)絡(luò)連接。

執(zhí)行斷路器圖案增加了穩(wěn)定性和靈活性,以一個(gè)系統(tǒng),提供穩(wěn)定性,而系統(tǒng)從故障中恢復(fù),并盡量減少此故障的對(duì)性能的影響。它可以幫助快速地拒絕對(duì)一個(gè)操作,即很可能失敗,而不是等待操作超時(shí)(或者不返回)的請(qǐng)求,以保持系統(tǒng)的響應(yīng)時(shí)間。如果斷路器提高每次改變狀態(tài)的時(shí)間的事件,該信息可以被用來(lái)監(jiān)測(cè)由斷路器保護(hù)系統(tǒng)的部件的健康狀況,或以提醒管理員當(dāng)斷路器跳閘,以在打開(kāi)狀態(tài)。

模式是可定制的,并且可以根據(jù)可能的故障的性質(zhì)進(jìn)行調(diào)整。例如,您可以申請(qǐng)?jiān)黾拥某瑫r(shí)時(shí)間為一個(gè)斷路器??梢苑胖迷诖蜷_(kāi)狀態(tài)的斷路器的幾秒鐘開(kāi)始,然后,如果故障一直沒(méi)有解決增加超時(shí)到幾分鐘的時(shí)間,等等。在某些情況下,而不是打開(kāi)狀態(tài)返回故障并提高了異常,也可能是有用的,返回一個(gè)缺省值,該值是有意義的應(yīng)用。

問(wèn)題和注意事項(xiàng)

在決定如何實(shí)現(xiàn)這個(gè)模式時(shí),您應(yīng)考慮以下幾點(diǎn):

  • 異常處理。通過(guò)斷路器調(diào)用操作的應(yīng)用程序必須準(zhǔn)備好處理,如果該操作是不可用的,可以被拋出的異常。在這樣的異常處理將特定應(yīng)用程序的方式。例如,一個(gè)應(yīng)用程序可以暫時(shí)降低其功能,調(diào)用替換操作來(lái)嘗試執(zhí)行相同的任務(wù)或獲得相同的數(shù)據(jù),或者報(bào)告該異常給用戶,并要求他們稍后再試。
  • 例外的類型。一個(gè)請(qǐng)求可能失敗的原因有多種,其中有一些可能指示更嚴(yán)重的類型的失效比其他。例如,一個(gè)請(qǐng)求可能會(huì)失敗,因?yàn)檫h(yuǎn)程服務(wù)已經(jīng)崩潰了,可能需要幾分鐘才能恢復(fù),或失敗可能是由于該服務(wù)被暫時(shí)超載造成的超時(shí)時(shí)間。一種斷路器可能能夠檢查發(fā)生的異常的類型,并根據(jù)這些異常的性質(zhì)調(diào)整策略。例如,它可能需要的超時(shí)異常更大數(shù)目的斷路器的開(kāi)狀態(tài)相比失敗次數(shù)跳閘由于服務(wù)是完全不可用。
  • 日志記錄。一個(gè)斷路器應(yīng)記錄所有失敗的請(qǐng)求(也可能是成功的請(qǐng)求),以使管理員能夠監(jiān)視它封裝了操作的健康。
  • 可恢復(fù)性。您應(yīng)該配置斷路器與之相匹配的是保護(hù)的操作可能恢復(fù)模式。例如,如果斷路器保持在打開(kāi)狀態(tài)下很長(zhǎng)一段時(shí)間,也可能產(chǎn)生異常,即使對(duì)于失敗的原因早已得到了解決。類似地,一個(gè)斷路器可以振蕩并降低應(yīng)用程序的響應(yīng)時(shí)間,如果它從打開(kāi)狀態(tài)到半開(kāi)狀態(tài)太快切換。
  • 測(cè)試失敗的操作。在打開(kāi)狀態(tài)下,而不是使用一個(gè)計(jì)時(shí)器來(lái)確定何時(shí)切換到半開(kāi)放狀態(tài)下,斷路器可代替周期性地查驗(yàn)遠(yuǎn)程服務(wù)或資源,以確定它是否已經(jīng)再次變得可用。這個(gè)平可以采取的企圖的形式援引了以前失敗的操作,也可以使用由遠(yuǎn)程服務(wù)提供的特殊操作專門用于測(cè)試服務(wù)的健康狀況,所描述的衛(wèi)生端點(diǎn)監(jiān)測(cè)圖案。
  • 手動(dòng)覆蓋。在一個(gè)系統(tǒng)中,如果恢復(fù)時(shí)間為一個(gè)失敗的操作是非??勺兊?,它可能是有利的,以提供一個(gè)手動(dòng)復(fù)位選項(xiàng),使管理員能夠強(qiáng)行關(guān)閉斷路器(和復(fù)位的故障計(jì)數(shù)器)。同樣,管理員可以強(qiáng)制斷路器進(jìn)入開(kāi)放狀態(tài)(并重新啟動(dòng)超時(shí)定時(shí)器),如果由斷路器保護(hù)動(dòng)作暫時(shí)不可用。
  • 并發(fā)。相同的電路斷路器可以通過(guò)大量的應(yīng)用程序的并行實(shí)例來(lái)訪問(wèn)。實(shí)施不應(yīng)該阻塞并發(fā)請(qǐng)求或添加過(guò)多的開(kāi)銷,以每次調(diào)用操作。
  • 資源分化。使用單個(gè)斷路器時(shí),一個(gè)類型的資源,如果??可能有多個(gè)潛在的獨(dú)立供應(yīng)商要小心。例如,在數(shù)據(jù)存儲(chǔ)器,其包括多個(gè)碎片,1分片可以是完全可訪問(wèn)的,而另一個(gè)是經(jīng)歷一個(gè)暫時(shí)的問(wèn)題。如果在這些情況下的錯(cuò)誤響應(yīng)被合二為一,應(yīng)用程序可能試圖訪問(wèn)一些碎片,即使發(fā)生故障的可能性高,同時(shí)獲得其他碎片,即使它是可能成功的可能被堵塞。
  • 加速斷路。有時(shí)失敗響應(yīng)可以包含足夠的信息用于斷路器的實(shí)施知道它應(yīng)當(dāng)立即跳閘并保持處于跳閘狀態(tài)的最小時(shí)間量。例如,從該過(guò)載的共享資源的錯(cuò)誤響應(yīng)可以指示立即重試時(shí)不推薦使用,并且該應(yīng)用程序應(yīng)代替再次嘗試在幾分鐘時(shí)間。

Note

HTTP 協(xié)議定義的“HTTP503 服務(wù)不可用”,它可以如所請(qǐng)求的服務(wù)是當(dāng)前不可用的特定的 Web 服務(wù)器上的被返回的響應(yīng)。此響應(yīng)可以包括附加信息,例如延遲的預(yù)期持續(xù)時(shí)間。

  • 重播失敗的請(qǐng)求。在打開(kāi)狀態(tài)下,而不是簡(jiǎn)單的故障很快,斷路器也可以記錄每個(gè)請(qǐng)求的詳細(xì)信息,以軸頸和安排這些請(qǐng)求時(shí),遠(yuǎn)程資源或服務(wù)變得可用重放。

  • 對(duì)外部服務(wù)不當(dāng)超時(shí)。電路斷路器可能無(wú)法充分保護(hù)的應(yīng)用程序,從失敗中配置有一個(gè)漫長(zhǎng)的超時(shí)時(shí)間對(duì)外服務(wù)業(yè)務(wù)。如果超時(shí)太長(zhǎng),運(yùn)行一個(gè)斷路器的螺紋可能被堵塞長(zhǎng)時(shí)間之前斷路器指示操作已失敗。在這個(gè)時(shí)候,許多其他的應(yīng)用程序?qū)嵗部梢試L試通過(guò)斷路器來(lái)調(diào)用服務(wù),并占用一個(gè)顯著的線程數(shù)之前,他們都失敗。

當(dāng)使用這個(gè)模式

使用這種模式:

  • 為了防止一個(gè)應(yīng)用程序試圖調(diào)用一個(gè)遠(yuǎn)程服務(wù)或訪問(wèn)共享資源,如果??該操作是極有可能失敗。

這種模式可能不適合:

  • 對(duì)于處理中的應(yīng)用程序訪問(wèn)本地專用資源,例如在存儲(chǔ)器內(nèi)數(shù)據(jù)結(jié)構(gòu)。在這種環(huán)境下,使用斷路器只會(huì)增加開(kāi)銷到您的系統(tǒng)。
  • 作為一個(gè)替代品來(lái)處理異常在應(yīng)用程序的業(yè)務(wù)邏輯。

例子

在 Web 應(yīng)用中,幾個(gè)頁(yè)面的已填充了從外部服務(wù)中檢索數(shù)據(jù)。如果該系統(tǒng)實(shí)現(xiàn)了最小的緩存,點(diǎn)擊率最高的為每個(gè)頁(yè)面都會(huì)導(dǎo)致往返服務(wù)。從 Web 應(yīng)用程序到服務(wù)的連接可以用一個(gè)超時(shí)時(shí)間段(通常為 60 秒)進(jìn)行配置,并且如果該服務(wù)沒(méi)有在這個(gè)時(shí)間響應(yīng)在每個(gè)網(wǎng)頁(yè)的邏輯將假設(shè)該服務(wù)不可用,并且拋出異常。

但是,如果服務(wù)失敗,系統(tǒng)非常繁忙,用戶可能會(huì)被迫等待異常發(fā)生時(shí)長(zhǎng)達(dá)60秒前。最終的資源,如內(nèi)存,連接和線程可能被耗盡,以防止其他用戶連接到系統(tǒng),即使它們沒(méi)有訪問(wèn)檢索業(yè)務(wù)數(shù)據(jù)的頁(yè)面。

通過(guò)添加更多的 Web 服務(wù)器和執(zhí)行負(fù)載均衡擴(kuò)展,系統(tǒng)可能會(huì)延誤的點(diǎn)資源趨于枯竭,但它不會(huì)解決問(wèn)題,因?yàn)橛脩粽?qǐng)求仍然會(huì)反應(yīng)遲鈍,所有的 Web 服務(wù)器仍然可以最終耗盡資源。

包裹連接到服務(wù),并檢索數(shù)據(jù)中的斷路器的邏輯可以幫助緩解這個(gè)問(wèn)題的影響,并且更優(yōu)雅處理服務(wù)故障。用戶請(qǐng)求仍然會(huì)失敗的,但它們將更加迅速地失敗,并且資源不會(huì)被阻塞。

CircuitBreaker類維護(hù)有關(guān)的對(duì)象,它實(shí)現(xiàn)下面的代碼所示ICircuitBreakerStateStore接口電路斷路器的狀態(tài)信息。

interface ICircuitBreakerStateStore  
{  
  CircuitBreakerStateEnum State { get; }  

  Exception LastException { get; }  

  DateTime LastStateChangedDateUtc { get; }  

  void Trip(Exception ex);  

  void Reset();  

  void HalfOpen();  

  bool IsClosed { get; }  
}  

狀態(tài)屬性指示斷路器的當(dāng)前狀態(tài),以及由 CircuitBreakerStateEnum 枚舉所定義的將是這些值中的一個(gè)程序,HalfOpen,或者已關(guān)閉。如果電路斷路器閉合,但如果其打開(kāi)或半開(kāi)的 IsClosed 屬性應(yīng)該是真實(shí)的。跳閘方法切換斷路器為打開(kāi)狀態(tài)的狀態(tài),并記錄該引起狀態(tài)變化的異常,與所發(fā)生的異常的日期和時(shí)間一起。該 LastException 和 LastStateChangedDateUtc 屬性返回此信息。復(fù)位方法關(guān)閉斷路器和 HalfOpen 方法將斷路器半開(kāi)。

在該實(shí)例中 InMemoryCircuitBreakerStateStore 類包含 ICircuitBreakerStateStore 接口的實(shí)現(xiàn)。該 CircuitBreaker 類創(chuàng)建這個(gè)類的一個(gè)實(shí)例來(lái)保存斷路器的狀態(tài)。

在 CircuitBreaker 類的 ExecuteAction 方法包裝的操作(在 Action 委托的形式)可能會(huì)失敗。當(dāng)該方法運(yùn)行時(shí),它首先檢查斷路器的狀態(tài)。如果它被關(guān)閉(當(dāng)?shù)?IsOpen 屬性,如果斷路器處于打開(kāi)狀態(tài)或半開(kāi),返回真,是假的)的 ExecuteAction 方法試圖調(diào)用 Action 委托。如果此操作失敗,異常處理程序執(zhí)行 TrackException 方法,用于設(shè)置該電路斷路器的狀態(tài)通過(guò)調(diào)用 InMemoryCircuitBreakerStateStore 物體的行程的方法打開(kāi)。下面的代碼示例強(qiáng)調(diào)了這一流程。

public class CircuitBreaker  
{  
  private readonly ICircuitBreakerStateStore stateStore =  
    CircuitBreakerStateStoreFactory.GetCircuitBreakerStateStore();  

  private readonly object halfOpenSyncObject = new object ();  
  ...  
  public bool IsClosed { get { return stateStore.IsClosed; } }  

  public bool IsOpen { get { return !IsClosed; } }  

  public void ExecuteAction(Action action)  
  {  
    ...  
    if (IsOpen)  
    {  
      // The circuit breaker is Open.  
      ... (see code sample below for details)  
    }  

    // The circuit breaker is Closed, execute the action.  
    try  
    {  
      action();  
    }  
    catch (Exception ex)  
    {  
      // If an exception still occurs here, simply   
      // re-trip the breaker immediately.  
      this.TrackException(ex);  

      // Throw the exception so that the caller can tell  
      // the type of exception that was thrown.  
      throw;  
    }  
  }  

  private void TrackException(Exception ex)  
  {  
    // For simplicity in this example, open the circuit breaker on the first exception.  
    // In reality this would be more complex. A certain type of exception, such as one  
    // that indicates a service is offline, might trip the circuit breaker immediately.   
    // Alternatively it may count exceptions locally or across multiple instances and  
    // use this value over time, or the exception/success ratio based on the exception  
    // types, to open the circuit breaker.  
    this.stateStore.Trip(ex);  
  }  
}  

下面的例子顯示了執(zhí)行,如果斷路器沒(méi)有關(guān)閉的代碼(從前面的例子中省略)。它如果斷路器已經(jīng)開(kāi)了一段時(shí)間長(zhǎng)于當(dāng)?shù)?OpenToHalfOpenWaitTime 字段中 CircuitBreaker 類中指定的時(shí)間首先檢查。如果是這種情況,則 ExecuteAction 方法設(shè)置斷路器半開(kāi),然后嘗試執(zhí)行該行動(dòng)代表所指定的操作。

如果操作成功,則斷路器復(fù)位到閉合狀態(tài)。如果操作失敗,則跳閘恢復(fù)到打開(kāi)狀態(tài),并且在發(fā)生被更新,以使斷路器將等待進(jìn)一步期間再次嘗試執(zhí)行該操作之前的異常所需的時(shí)間。

如果斷路器至今只有開(kāi)放的時(shí)間很短,小于 OpenToHalfOpenWaitTime 值時(shí),ExecuteAction 方法簡(jiǎn)單地拋出 CircuitBreakerOpenException 異常和返回引發(fā)的斷路器轉(zhuǎn)換到打開(kāi)狀態(tài)的誤差。

此外,為了防止斷路器試圖執(zhí)行并發(fā)呼叫的操作,同時(shí)它是半開(kāi)的,它使用一個(gè)鎖。兼試圖調(diào)用該操作會(huì)如果斷路器是公開(kāi)進(jìn)行處理,如后所述,它會(huì)失敗并異常。

...  
  if (IsOpen)  
  {  
    // The circuit breaker is Open. Check if the Open timeout has expired.  
    // If it has, set the state to HalfOpen. Another approach may be to simply   
    // check for the HalfOpen state that had be set by some other operation.  
    if (stateStore.LastStateChangedDateUtc + OpenToHalfOpenWaitTime < DateTime.UtcNow)  
    {  
      // The Open timeout has expired. Allow one operation to execute. Note that, in  
      // this example, the circuit breaker is simply set to HalfOpen after being   
      // in the Open state for some period of time. An alternative would be to set   
      // this using some other approach such as a timer, test method, manually, and   
      // so on, and simply check the state here to determine how to handle execution  
      // of the action.   
      // Limit the number of threads to be executed when the breaker is HalfOpen.  
      // An alternative would be to use a more complex approach to determine which  
      // threads or how many are allowed to execute, or to execute a simple test   
      // method instead.  
      bool lockTaken = false;  
      try  
      {  
        Monitor.TryEnter(halfOpenSyncObject, ref lockTaken)  
        if (lockTaken)  
        {  
          // Set the circuit breaker state to HalfOpen.  
          stateStore.HalfOpen();  

          // Attempt the operation.  
          action();  

          // If this action succeeds, reset the state and allow other operations.  
          // In reality, instead of immediately returning to the Open state, a counter  
          // here would record the number of successful operations and return the  
          // circuit breaker to the Open state only after a specified number succeed.  
          this.stateStore.Reset();  
          return;  
        }  
        catch (Exception ex)  
        {  
          // If there is still an exception, trip the breaker again immediately.  
          this.stateStore.Trip(ex);  

          // Throw the exception so that the caller knows which exception occurred.  
          throw;  
        }  
        finally  
        {  
          if (lockTaken)  
          {  
            Monitor.Exit(halfOpenSyncObject);  
          }  
        }  
      }  
    }  
    // The Open timeout has not yet expired. Throw a CircuitBreakerOpen exception to  
    // inform the caller that the caller that the call was not actually attempted,   
    // and return the most recent exception received.  
    throw new CircuitBreakerOpenException(stateStore.LastException);  
  }  
  ...  

使用 CircuitBreaker 對(duì)象,以保護(hù)操作時(shí),應(yīng)用程序創(chuàng)建的 CircuitBreaker 類的一個(gè)實(shí)例,并調(diào)用 ExecuteAction 方法,指定的操作被作為參數(shù)來(lái)執(zhí)行。該應(yīng)用程序應(yīng)該準(zhǔn)備,如果操作失敗,因?yàn)閿嗦菲魈幱诖蜷_(kāi)狀態(tài),以趕上 CircuitBreakerOpenException 例外。下面的代碼顯示了一個(gè)示例:

var breaker = new CircuitBreaker();  

try  
{  
  breaker.ExecuteAction(() =>  
  {  
    // Operation protected by the circuit breaker.  
    ...  
  });  
}  
catch (CircuitBreakerOpenException ex)  
{  
  // Perform some different action when the breaker is open.  
  // Last exception details are in the inner exception.  
  ...  
}  
catch (Exception ex)  
{  
  ...  
}