(8)彈力設計篇之“熔斷設計”

1、熔斷設計:正常、故障和、故障后檢測(故障是否已被修復的場景)

2、介紹了 Netflix 的 Hystrix 對熔斷的實現

3、熔斷設計的幾個重點

熔斷機制借鑒于我們電閘上的 " 保險絲 ",當電壓有問題時(比如短路),自動跳閘,此時電路就會斷開,我們的電器就會受到保護。

重試機制,如果錯誤太多,或是在短時間內得不到修復,重試也沒有意義了,此時應該開啟我們的熔斷操作,保護后端不會過載。

熔斷設計

防止不斷地嘗試可能會失敗的操作,使應用程序繼續執行而不用等待修正錯誤,或者浪費 CPU 時間去等待長時間的超時產生。熔斷器模式也可以使應用程序能夠診斷錯誤是否已經修正如果已經修正,再次嘗試調用操作

熔斷器模式就像是那些容易導致錯誤的操作的一種代理。這種代理能夠記錄最近調用發生錯誤的次數,然后決定允許操作繼續,或者立即返回錯誤。

(本圖來自 Martin Fowler 的 Circuit Breaker)

熔斷器可以使用狀態機來實現,內部模擬以下幾種狀態。

閉合(Closed)狀態:失敗次數加 1。給定時間內超過允許失敗的閾值,切換到斷開 (Open) 狀態。此時開啟了一個超時時鐘,當該時鐘超過了該時間,切換到半斷開(Half-Open)狀態。該超時時間的設定是給了系統一次機會來修正導致調用失敗的錯誤,以回到正常工作的狀態。在 Closed 狀態下,錯誤計數器是基于時間的。在特定的時間間隔內自動重置。這能夠防止由于某次的偶然錯誤導致熔斷器進入斷開狀態。也可以基于連續失敗的次數。

斷開 (Open) 狀態:在該狀態下,對應用程序的請求會立即返回錯誤響應,而不調用后端的服務。這樣也許比較粗暴,有些時候,我們可以 cache 住上次成功請求,直接返回緩存(當然,這個緩存放在本地內存就好了),如果沒有緩存再返回錯誤(緩存的機制最好用在全站一樣的數據,而不是用在不同的用戶間不同的數據,因為后者需要緩存的數據有可能會很多)。

半開(Half-Open)狀態允許應用程序一定數量的請求去調用服務。如果這些請求對服務的調用成功,那么可以認為之前導致調用失敗的錯誤已經修正,此時熔斷器切換到閉合狀態 (并且將錯誤計數器重置)。

如果這一定數量的請求有調用失敗的情況,則認為導致之前調用失敗的問題仍然存在,熔斷器切回到斷開狀態,然后重置計時器來給系統一定的時間來修正錯誤。半斷開狀態能夠有效防止正在恢復中的服務被突然而來的大量請求再次拖垮。

實現熔斷器模式使得系統更加穩定和有彈性,在系統從錯誤中恢復的時候提供穩定性,并且減少了錯誤對系統性能的影響。它通過快速地拒絕那些試圖有可能會導致錯誤的服務調用,而不會去等待操作超時或者永遠不返回結果來提高系統的響應時間。

如果熔斷器設計模式在每次狀態切換的時候會發出一個事件,這種信息可以用來監控服務的運行狀態,能夠通知管理員在熔斷器切換到斷開狀態時進行處理。

二、Netflix 的開源項目Hystrix中的熔斷的實現邏輯(其出處在這里)。

從這個流程圖中,可以看到:

有請求來了,首先 allowRequest() 函數判斷是否在熔斷中,如果不是則放行,如果是的話,還要看有沒有到達一個熔斷時間片,如果熔斷時間片到了,也放行,否則直接返回出錯。

每次調用都有兩個函數 markSuccess(duration) 和 markFailure(duration) 來統計一下在一定的 duration 內有多少調用是成功還是失敗的。

判斷是否熔斷的條件 isOpen(),是計算一下 failure/(success+failure) 當前的錯誤率,如果高于一個閾值,那么打開熔斷,否則關閉。

Hystrix 會在內存中維護一個數組,其中記錄著每一個周期的請求結果的統計。超過時長長度的元素會被刪除掉。

三、熔斷設計的重點

在實現熔斷器模式的時候,以下這些因素需可能需要考慮。

錯誤的類型。根據不同的錯誤情況來調整相應的策略。重試一樣,需要對返回的錯誤進行識別。一些錯誤先走重試的策略(比如限流,或是超時),重試幾次后再打開熔斷。一些錯誤是遠程服務掛掉,恢復時間比較長;這種錯誤不必走重試,可以直接打開熔斷策略。

日志監控。熔斷器應該能夠記錄所有失敗的請求,以及一些可能會嘗試成功的請求,使得管理員能夠監控使用熔斷器保護的服務的執行情況。

測試服務是否可用。在斷開狀態下,熔斷器可以采用定期地 ping 一下遠程的服務的健康檢查接口,來判斷服務是否恢復,而不是使用計時器來自動切換到半開狀態。這樣做的一個好處是,在服務恢復的情況下,不需要真實的用戶流量就可以把狀態從半開狀態切回關閉狀態。否則在半開狀態下,即便服務已恢復了,也需要用戶真實的請求來恢復,這會影響用戶的真實請求。

手動重置。在系統中對于失敗操作的恢復時間是很難確定的,提供一個手動重置功能能夠使得管理員可以手動地強制將熔斷器切換到閉合狀態。同樣的,如果受熔斷器保護的服務暫時不可用的話,管理員能夠強制將熔斷器設置為斷開狀態。

并發問題。不應該阻塞并發的請求或者增加每次請求調用的負擔。尤其是其中的對調用結果的統計,一般來說會成為一個共享的數據結構,這個會導致有鎖的情況。在這種情況下,最好使用一些無鎖的數據結構,或是 atomic 的原子操作。這樣會帶來更好的性能。

資源分區。熔斷器只對有問題的分區進行熔斷,而不是整體。比如,數據庫的分庫分表,某個分區可能出現問題,而其它分區還可用。單一的熔斷器會把所有的分區訪問給混為一談,從而,一旦開始熔斷,那么所有的分區都會受到熔斷影響。或是出現一會兒熔斷一會兒又好,來來回回的情況。

重試錯誤的請求。有時候,錯誤和請求的數據和參數有關系,所以,記錄下出錯的請求,在半開狀態下重試能夠準確地知道服務是否真的恢復。當然,這需要被調用端支持冪等調用,否則會出現一個操作被執行多次的副作用。

也歡迎你分享一下你實現過的熔斷使用了怎樣的算法?實現的過程中遇到過什么坑?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,837評論 18 139
  • 專業考題類型管理運行工作負責人一般作業考題內容選項A選項B選項C選項D選項E選項F正確答案 變電單選GYSZ本規程...
    小白兔去釣魚閱讀 9,049評論 0 13
  • ORA-00001: 違反唯一約束條件 (.) 錯誤說明:當在唯一索引所對應的列上鍵入重復值時,會觸發此異常。 O...
    我想起個好名字閱讀 5,407評論 0 9
  • Java繼承關系初始化順序 父類的靜態變量-->父類的靜態代碼塊-->子類的靜態變量-->子類的靜態代碼快-->父...
    第六象限閱讀 2,172評論 0 9
  • 無戒365·婚姻育兒·日記專題聯合征文 “要為重活的現在高興,不為死去的過去憂傷”。小艾帶著微醉的眼神,堅定有力地...
    蘭汐蘭閱讀 383評論 3 7