採用斷路器設計模式來保護軟體
程式設計師的人生就像在一個快車道上行駛。幾周甚至幾小時完成某些特性編碼,打包測試沒有問題,蓋上QA認證,程式碼部署到生產環境。接著最壞的事情發生了,部署的軟體在執行中掛掉了。用墨菲法則來說,就是“會出錯的,終將出錯”。但是,如果我們寫程式碼時就能考慮到這些情況會怎樣?
那麼我們應對不好的事情並將其轉變為好的事情呢?
電子技術拯救了我們
我至今記得我和我哥因為電湧不得不更換家裡的保險絲情景,那時我甚至對事件的嚴重程度一無所知,而他卻是電力方面的小能手。保險絲完全燒壞了,但它卻保護了我家的電視機。在電子工程領域,保險絲和斷路器用(Circuit Breaker)來處理這樣的問題,超大功率可能帶來一些嚴重的破壞,毀壞電子裝置甚至燒掉整個屋子。保險絲包含一個小電線絲,電量過大時就會融化,就像燒掉的電燈泡,阻止危險的電流,保障電器和房屋安全。
保險絲演變成斷路器,通常利用電磁鐵就可以斷開電路,而不用燒掉它,這樣斷路器就可以重置反覆地用。不過,它們的功能都是一樣的,檢測負載,接著迅速停止工作,保全其它部分不受破壞。
回過頭再想,這是一個多麼神奇的概念。僅僅壞掉某個控制元件——保險絲徹底壞掉,就可以避免了整個系統嚴重的損壞。多虧電湧後保險絲自熔,保住了電視機。那麼為何我們不能在軟體裡面做同樣的事情?壞事發生後,軟體中的某個控制元件會迅速停止工作。模仿現實生活中的場景,我們創造了斷路器設計模式。
在分散式系統中,某些故障是短暫的,通過快速連續重試就可以解決問題;但在某些場景中,關鍵依賴的連線丟失了,短時間無法恢復。比如,某個應用失去了與雲中的持續化儲存連線。在這樣的場景中,關閉服務就可以避免錯誤的資料處理過程、甚至資料丟失或者級聯故障,進而防止對系統其它部分的進一步損壞。
藉助於迅速停止工作,運維部門就可以容易地進行監控和響應。在他們重視起來之前,那些徒勞嘗試重新連線的服務看起來仍然是正常的,因為本應該拉響的警報沒有響起。倘若某個服務在恰當的時候徹底失效,警告燈熄滅了,運維就會知曉問題所在,及時做出響應。
斷路器設計模式
在系統中可重用基礎架構實現斷路器設計模式是很容易實現的,它是這麼發揮作用的:
1 定義一個可重用的CircuitBreaker類,包含Trip和Reset方法,以及斷路器跳閘就可以呼叫的action
2 利用CircuitBreaker去監控系統依賴。針對每個單一的故障,斷路器跳閘就會將其設定在一種佈防狀態,就像電湧出現時那樣。
3 倘若接下來在特定的時間視窗內嘗試成功,那麼就重置此斷路器,一切恢復正常。
4 倘若斷路器沒有在特定的時間重置,異常會持續發生,此時斷路器就會呼叫你提供的action。你可以在斷路器跳閘時選擇快速停止工作(終止程式)或者其他action。
應用案例
本例中ExternalServiceAdapter類幫助系統與外部依賴建立連線。或許有個網路程式產生請求頻繁地執行DoStuff操作。一旦執行,若此時GetConnection執行出錯,異常就會發生,斷路器就會被跳閘。倘若連線重新建立起來,斷路器就會被重置。不過連線異常持續發生時,斷路器就會跳閘,特定的跳閘action就會執行,在本例中將會迅速停止工作。
public class ExternalServiceAdapter { private CircuitBreaker circuitBreaker; public ExternalServiceAdapter() { circuitBreaker = new CircuitBreaker("CheckConnection", /*斷路器名稱 */ exception => /* 一旦斷路器跳閘此action就會被呼叫 */ { Console.WriteLine("Circuit breaker tripped! Fail fast!"); //終止程式,略過接下來的任何try/finally塊或者finalizers Environment.FailFast(exception.Message); }, 3, /* 斷路器跳閘前的最大閾值*/ TimeSpan.FromSeconds(2)); /* Time to wait between each try before attempting to trip the circuit breaker */ } public void DoStuff() { var externalService = GetConnection(); externalService.DoStuff(); } ConnectionDependency GetConnection() { try { var newConnection = new ConnectionDependency(); circuitBreaker.Reset(); return newConnection; } catch (Exception exception) { circuitBreaker.Trip(exception); throw; } } }
斷路器模式簡單實現
using System; using System.Threading; public class CircuitBreaker { public CircuitBreaker(string name, /*操作名稱*/ Action<Exception> tripAction, /* 一旦斷路器跳閘action就會被呼叫*/ int maxTimesToRetry, /* 斷路器跳閘前重試的時間*/ TimeSpan delayBetweenRetries /*每一次重試的時間間隔*/) { this.name = name; this.tripAction = tripAction; this.maxTimesToRetry = maxTimesToRetry; this.delayBetweenRetries = delayBetweenRetries; // 一旦使用者迫使斷路器跳閘,計時器就會開啟 timer = new Timer(CircuitBreakerTripped, null, Timeout.Infinite, (int)delayBetweenRetries.TotalMilliseconds); } public void Reset() { var oldValue = Interlocked.Exchange(ref failureCount, 0); timer.Change(Timeout.Infinite, Timeout.Infinite); Console.WriteLine("The circuit breaker for {0} is now disarmed", name); } public void Trip(Exception ex) { lastException = ex; var newValue = Interlocked.Increment(ref failureCount); if (newValue == 1) { // 開啟重試計時器. timer.Change(delayBetweenRetries, TimeSpan.FromMilliseconds(-1)); // 記錄已觸發的斷路器. Console.WriteLine("The circuit breaker for {0} is now in the armed state", name); } } void CircuitBreakerTripped(object state) { Console.WriteLine("Check to see if we need to trip the circuit breaker. Retry:{0}", failureCount); if (Interlocked.Increment(ref failureCount) > maxTimesToRetry) { Console.WriteLine("The circuit breaker for {0} is now tripped. Calling specified action", name); tripAction(lastException); return; } timer.Change(delayBetweenRetries, TimeSpan.FromMilliseconds(-1)); } readonly string name; readonly int maxTimesToRetry; long failureCount; readonly Action<Exception> tripAction; Exception lastException; readonly TimeSpan delayBetweenRetries; readonly Timer timer; }
斷路器單元測試
[TestFixture] public class CircuitBreakerTests { [Test] public void When_the_circuit_breaker_is_tripped_the_trip_action_is_called_after_reaching_max_threshold() { bool circuitBreakerTripActionCalled = false; var connectionException = new Exception("Something bad happened."); var circuitBreaker = new CircuitBreaker("CheckServiceConnection", exception => { Console.WriteLine("Circuit breaker tripped - fail fast"); circuitBreakerTripActionCalled = true; // You would normally fail fast here in the action to faciliate the process shutdown by calling: // Environment.FailFast(connectionException.Message); }, 3, TimeSpan.FromSeconds(1)); circuitBreaker.Trip(connectionException); System.Threading.Thread.Sleep(5000); Assert.IsTrue(circuitBreakerTripActionCalled); } [Test] public void When_the_circuit_breaker_is_reset_the_trip_action_is_not_called() { bool circuitBreakerTripActionCalled = false; var connectionException = new Exception("Something bad happened."); var circuitBreaker = new CircuitBreaker("CheckServiceConnection", exception => { Console.WriteLine("Circuit breaker tripped - fail fast"); circuitBreakerTripActionCalled = true; // You would normally fail fast here in the action to faciliate the process shutdown by calling: // Environment.FailFast(connectionException.Message); }, 3, TimeSpan.FromSeconds(2)); circuitBreaker.Trip(connectionException); System.Threading.Thread.Sleep(1000); circuitBreaker.Reset(); Assert.False(circuitBreakerTripActionCalled); } }
上面程式碼案例採用Console.WriteLine,你可以選擇自己喜歡的logger。
最後結語
斷路器是現代社會重要的組成部分,可以說是最重要的安全裝置之一。不論是一個熔化的保險絲或者跳閘的斷路器,其背後都有充足的理由。
監控重要的資源,一旦它們無法響應,就迅速停止工作,進而確保整個運維團隊做出正確的響應。
如果你想對這些設計模式做進一步瞭解,請看Michael T. Nygard 的《Release It》,它是本相當不錯的讀物。
相關文章
- 軟體保護
- 熔斷器設計模式設計模式
- 軟體保護建議
- 軟體設計模式學習(二十)迭代器模式設計模式
- 軟體設計模式系列之十八——迭代器模式設計模式
- 計算機軟體保護條例 (轉)計算機
- 馬達開關 ( 電動機保護型斷路器 )
- 軟體設計模式設計模式
- 軟體設計模式系列之十七——直譯器模式設計模式
- 如何保護Windows網路免受勒索軟體攻擊Windows
- 保護模式模式
- 勒索軟體保護即服務(RPaaS)時代已到來
- 為自動駕駛保駕護航—談談主流中介軟體設計自動駕駛
- 隱私保護軟體——蜘蛛密友
- Easy App Locker for Mac(Mac應用密碼保護軟體)APPMac密碼
- 網路安全行政命令:保護軟體供應鏈
- 23種軟體設計模式設計模式
- 電路設計軟體
- 採用Native引導方式的.Net加密保護加密
- DG的保護模式模式
- 真實模式和保護模式模式
- 軟體設計模式學習(十八)命令模式設計模式
- 軟體設計模式系列之十六——命令模式設計模式
- Java EE的斷路器API設計JavaAPI
- 軟體設計原則與模式模式
- 基於開源軟體、採用創新模式發展國產基礎軟體模式
- 妙用設計模式來設計一個校驗器設計模式
- 軟體設計模式學習(十三)裝飾模式設計模式
- 軟體設計模式系列之十二——外觀模式設計模式
- 軟體設計模式系列之十三——享元模式設計模式
- 軟體設計模式系列之十九——中介者模式設計模式
- 軟體設計模式系列之十——組合模式設計模式
- 直播軟體搭建,Java設計模式單例模式Java設計模式單例
- 用安全計算怎麼保護關鍵業務?快來學習!!!
- 保護性暫停模式模式
- dataguard三種保護模式模式
- DataGuard切換保護模式模式
- Windows的保護模式 (轉)Windows模式