<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 云計算設計模式(二)——斷路器模式 處理故障連接到遠程服務或資源時,可能需要耗費大量的時間。這種模式可以提高應用程序的穩定性和靈活性。 ## 背景和問題 在分布式環境中,如在云,其中,應用程序執行訪問遠程資源和服務的操作,有可能對這些操作的失敗是由于瞬時故障,如慢的網絡連接,超時,或者被過度使用的資源或暫時不可用。這些故障一般之后的短時間內糾正自己,和一個強大的云應用應該準備使用的策略來處理它們,例如,通過重試模式進行說明。 但是,也可以是其中的故障是由于那些不容易預見的突發事件的情況下,這可能需要更長的時間來糾正。這些故障從連接的部分損失到服務的完整的故障范圍的嚴重程度。在這種情況下,它可能是毫無意義的應用,不斷重試執行的操作是不太可能成功,而不是應用程序應該很快接受該操作已失敗,并相應地處理這個故障。 此外,如果一個服務是非常繁忙的,在系統中的一個部分出現故障可能會導致連鎖故障。例如,調用一個服務的操作可被配置成實現一個超時,如果該服務無法在這段時間內響應一個失敗消息答復。然而,這一策略可能導致許多并發請求的相同的操作,直到超時時段期滿被阻止。這些被禁止的請求可能會持有關鍵系統資源,如內存,線程,數據庫連接等。因此,這些資源可能會耗盡,從而導致該系統的其他可能無關的部件,需要使用相同的資源的失敗。在這些情況下,這將是優選的操作立即失敗,并且只嘗試調用服務,如果它是可能成功。注意,設置一個較短的超時可能有助于解決此問題,但在超時不應該如此之短,以致操作失敗的大部分時間,即使該請求到服務最終會成功。 ## 解決方案 該斷路器圖案可以防止一個應用程序多次試圖執行一個操作,即很可能失敗,允許它繼續而不等待故障恢復或者浪費 CPU 周期,而它確定該故障是持久的。斷路器模式也使應用程序能夠檢測故障是否已經解決。如果問題似乎已經得到糾正??,應用程序可以嘗試調用操作。 **注意** 斷路器圖案的目的是從該重試模式的不同。重試模式使應用程序可以重試操作以期望它會成功。斷路器圖案防止應用程序執行一個操作,即很可能失敗。一個應用程序可以通過使用重試模式,通過一個斷路器調用操作結合這兩種模式。然而,在重試邏輯應該是由斷路器返回任何異常敏感和放棄重試次數,如果斷路器指示故障不是瞬時的。 斷路器充當可能失敗操作的代理。代理應監測最近發生的故障數量,然后使用這個信息來決定是否允許該操作繼續進行,或簡單地立即返回一個異常。 代理可以被實現為狀態機與模擬的電路斷路器的功能如下狀態: **關閉**:從應用程序的請求是通過對操作進行路由。代理保持最近的失敗次數的計數,并且如果該呼叫到操作不成功,則代理遞增該計數。如果最近的失敗次數超過了一個給定時間周期內的規定的閾值時,該代理將被置于打開狀態。在這一點上的代理啟動一個超時定時器,當該定時器期滿的代理放置到半開放狀態。 **注意** 超時定時器的目的是為了給系統時間,糾正允許應用程序嘗試再次執行該操作之前導致失敗的問題。 - 打開:從應用程序請求立即失敗和異常返回給應用程序。 - 半開放:從應用程序請求的數量有限允許通過并調用運行。如果這些請求是成功的,則假定先前導致故障的故障已修復和斷路器切換到閉合狀態(故障計數器被復位)。如果任何請求失敗,斷路器假設故障仍然存在,因此恢復到打開狀態,并重新啟動超時定時器,系統的時間再延長,從故障中恢復。 **注意** 半開的狀態是很有用的,以防止恢復服務,從突然被淹沒的請求。作為服務恢復,也可能是能夠支持請求的限制音量,直到恢復完成,但在恢復過程中,海量的工作可能會導致服務超時或再次失敗。 下圖展示出了用于一個可能的實現的電路斷路器的狀態。 ![](https://box.kancloud.cn/2015-09-21_55ffa42e50282.png) 圖 1 - 斷路器狀態 需要注意的是,在圖 1 中,所用的封閉狀態下的失敗計數器是基于時間的。它以定期自動復位。這有助于防止斷路器進入打開狀態,如果它經受偶然的失敗;這使斷路器跳閘進入打開狀態的故障閾值時,故障的指定數量的指定的時間間隔期間發生的僅達到。所使用的半開狀態下的成功計數器記錄成功嘗試調用的操作的數量。斷路器恢復到封閉狀態后的連續操作調用中指定數量的已成功。如果任何調用失敗時,斷路器立即進入打開狀態,并且成功的計數器將其進入半開狀態下一次復位。 ### Note 如何將系統恢復從外部處理,可能通過恢復或重新啟動故障部件或修理的網絡連接。 執行斷路器圖案增加了穩定性和靈活性,以一個系統,提供穩定性,而系統從故障中恢復,并盡量減少此故障的對性能的影響。它可以幫助快速地拒絕對一個操作,即很可能失敗,而不是等待操作超時(或者不返回)的請求,以保持系統的響應時間。如果斷路器提高每次改變狀態的時間的事件,該信息可以被用來監測由斷路器保護系統的部件的健康狀況,或以提醒管理員當斷路器跳閘,以在打開狀態。 模式是可定制的,并且可以根據可能的故障的性質進行調整。例如,您可以申請增加的超時時間為一個斷路器。可以放置在打開狀態的斷路器的幾秒鐘開始,然后,如果故障一直沒有解決增加超時到幾分鐘的時間,等等。在某些情況下,而不是打開狀態返回故障并提高了異常,也可能是有用的,返回一個缺省值,該值是有意義的應用。 ## 問題和注意事項 在決定如何實現這個模式時,您應考慮以下幾點: - 異常處理。通過斷路器調用操作的應用程序必須準備好處理,如果該操作是不可用的,可以被拋出的異常。在這樣的異常處理將特定應用程序的方式。例如,一個應用程序可以暫時降低其功能,調用替換操作來嘗試執行相同的任務或獲得相同的數據,或者報告該異常給用戶,并要求他們稍后再試。 - 例外的類型。一個請求可能失敗的原因有多種,其中有一些可能指示更嚴重的類型的失效比其他。例如,一個請求可能會失敗,因為遠程服務已經崩潰了,可能需要幾分鐘才能恢復,或失敗可能是由于該服務被暫時超載造成的超時時間。一種斷路器可能能夠檢查發生的異常的類型,并根據這些異常的性質調整策略。例如,它可能需要的超時異常更大數目的斷路器的開狀態相比失敗次數跳閘由于服務是完全不可用。 - 日志記錄。一個斷路器應記錄所有失敗的請求(也可能是成功的請求),以使管理員能夠監視它封裝了操作的健康。 - 可恢復性。您應該配置斷路器與之相匹配的是保護的操作可能恢復模式。例如,如果斷路器保持在打開狀態下很長一段時間,也可能產生異常,即使對于失敗的原因早已得到了解決。類似地,一個斷路器可以振蕩并降低應用程序的響應時間,如果它從打開狀態到半開狀態太快切換。 - 測試失敗的操作。在打開狀態下,而不是使用一個計時器來確定何時切換到半開放狀態下,斷路器可代替周期性地查驗遠程服務或資源,以確定它是否已經再次變得可用。這個平可以采取的企圖的形式援引了以前失敗的操作,也可以使用由遠程服務提供的特殊操作專門用于測試服務的健康狀況,所描述的衛生端點監測圖案。 - 手動覆蓋。在一個系統中,如果恢復時間為一個失敗的操作是非常可變的,它可能是有利的,以提供一個手動復位選項,使管理員能夠強行關閉斷路器(和復位的故障計數器)。同樣,管理員可以強制斷路器進入開放狀態(并重新啟動超時定時器),如果由斷路器保護動作暫時不可用。 - 并發。相同的電路斷路器可以通過大量的應用程序的并行實例來訪問。實施不應該阻塞并發請求或添加過多的開銷,以每次調用操作。 - 資源分化。使用單個斷路器時,一個類型的資源,如果??可能有多個潛在的獨立供應商要小心。例如,在數據存儲器,其包括多個碎片,1分片可以是完全可訪問的,而另一個是經歷一個暫時的問題。如果在這些情況下的錯誤響應被合二為一,應用程序可能試圖訪問一些碎片,即使發生故障的可能性高,同時獲得其他碎片,即使它是可能成功的可能被堵塞。 - 加速斷路。有時失敗響應可以包含足夠的信息用于斷路器的實施知道它應當立即跳閘并保持處于跳閘狀態的最小時間量。例如,從該過載的共享資源的錯誤響應可以指示立即重試時不推薦使用,并且該應用程序應代替再次嘗試在幾分鐘時間。 ### Note HTTP 協議定義的“HTTP503 服務不可用”,它可以如所請求的服務是當前不可用的特定的 Web 服務器上的被返回的響應。此響應可以包括附加信息,例如延遲的預期持續時間。 - 重播失敗的請求。在打開狀態下,而不是簡單的故障很快,斷路器也可以記錄每個請求的詳細信息,以軸頸和安排這些請求時,遠程資源或服務變得可用重放。 - 對外部服務不當超時。電路斷路器可能無法充分保護的應用程序,從失敗中配置有一個漫長的超時時間對外服務業務。如果超時太長,運行一個斷路器的螺紋可能被堵塞長時間之前斷路器指示操作已失敗。在這個時候,許多其他的應用程序實例也可以嘗試通過斷路器來調用服務,并占用一個顯著的線程數之前,他們都失敗。 ## 當使用這個模式 使用這種模式: - 為了防止一個應用程序試圖調用一個遠程服務或訪問共享資源,如果??該操作是極有可能失敗。 這種模式可能不適合: - 對于處理中的應用程序訪問本地專用資源,例如在存儲器內數據結構。在這種環境下,使用斷路器只會增加開銷到您的系統。 - 作為一個替代品來處理異常在應用程序的業務邏輯。 ## 例子 在 Web 應用中,幾個頁面的已填充了從外部服務中檢索數據。如果該系統實現了最小的緩存,點擊率最高的為每個頁面都會導致往返服務。從 Web 應用程序到服務的連接可以用一個超時時間段(通常為 60 秒)進行配置,并且如果該服務沒有在這個時間響應在每個網頁的邏輯將假設該服務不可用,并且拋出異常。 但是,如果服務失敗,系統非常繁忙,用戶可能會被迫等待異常發生時長達60秒前。最終的資源,如內存,連接和線程可能被耗盡,以防止其他用戶連接到系統,即使它們沒有訪問檢索業務數據的頁面。 通過添加更多的 Web 服務器和執行負載均衡擴展,系統可能會延誤的點資源趨于枯竭,但它不會解決問題,因為用戶請求仍然會反應遲鈍,所有的 Web 服務器仍然可以最終耗盡資源。 包裹連接到服務,并檢索數據中的斷路器的邏輯可以幫助緩解這個問題的影響,并且更優雅處理服務故障。用戶請求仍然會失敗的,但它們將更加迅速地失敗,并且資源不會被阻塞。 該`CircuitBreaker`類維護有關的對象,它實現下面的代碼所示`ICircuitBreakerStateStore`接口電路斷路器的狀態信息。 ~~~ interface ICircuitBreakerStateStore { CircuitBreakerStateEnum State { get; } ? Exception LastException { get; } ? DateTime LastStateChangedDateUtc { get; } ? void Trip(Exception ex); ? void Reset(); ? void HalfOpen(); ? bool IsClosed { get; } } ~~~ 狀態屬性指示斷路器的當前狀態,以及由 CircuitBreakerStateEnum 枚舉所定義的將是這些值中的一個程序,HalfOpen,或者已關閉。如果電路斷路器閉合,但如果其打開或半開的 IsClosed 屬性應該是真實的。跳閘方法切換斷路器為打開狀態的狀態,并記錄該引起狀態變化的異常,與所發生的異常的日期和時間一起。該 LastException 和 LastStateChangedDateUtc 屬性返回此信息。復位方法關閉斷路器和 HalfOpen 方法將斷路器半開。 在該實例中 InMemoryCircuitBreakerStateStore 類包含 ICircuitBreakerStateStore 接口的實現。該 CircuitBreaker 類創建這個類的一個實例來保存斷路器的狀態。 在 CircuitBreaker 類的 ExecuteAction 方法包裝的操作(在 Action 委托的形式)可能會失敗。當該方法運行時,它首先檢查斷路器的狀態。如果它被關閉(當地 IsOpen 屬性,如果斷路器處于打開狀態或半開,返回真,是假的)的 ExecuteAction 方法試圖調用 Action 委托。如果此操作失敗,異常處理程序執行 TrackException 方法,用于設置該電路斷路器的狀態通過調用 InMemoryCircuitBreakerStateStore 物體的行程的方法打開。下面的代碼示例強調了這一流程。 ~~~ 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); } } ~~~ 下面的例子顯示了執行,如果斷路器沒有關閉的代碼(從前面的例子中省略)。它如果斷路器已經開了一段時間長于當地 OpenToHalfOpenWaitTime 字段中 CircuitBreaker 類中指定的時間首先檢查。如果是這種情況,則 ExecuteAction 方法設置斷路器半開,然后嘗試執行該行動代表所指定的操作。 如果操作成功,則斷路器復位到閉合狀態。如果操作失敗,則跳閘恢復到打開狀態,并且在發生被更新,以使斷路器將等待進一步期間再次嘗試執行該操作之前的異常所需的時間。 如果斷路器至今只有開放的時間很短,小于 OpenToHalfOpenWaitTime 值時,ExecuteAction 方法簡單地拋出 CircuitBreakerOpenException 異常和返回引發的斷路器轉換到打開狀態的誤差。 此外,為了防止斷路器試圖執行并發呼叫的操作,同時它是半開的,它使用一個鎖。兼試圖調用該操作會如果斷路器是公開進行處理,如后所述,它會失敗并異常。 ~~~ ... 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 對象,以保護操作時,應用程序創建的 CircuitBreaker 類的一個實例,并調用 ExecuteAction 方法,指定的操作被作為參數來執行。該應用程序應該準備,如果操作失敗,因為斷路器處于打開狀態,以趕上 CircuitBreakerOpenException 例外。下面的代碼顯示了一個示例: ~~~ 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) { ... } ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看