處理暫時性錯誤的建議
適用於此 Azure 架構完善的架構可靠性檢查清單建議:
RE:07 | 藉由實作自我保存和自我修復措施,強化工作負載的復原能力和復原能力。 使用基礎結構式可靠性模式和軟體型設計模式,來處理元件失敗和暫時性錯誤,以將功能建置至解決方案。 在系統中建置功能,以偵測解決方案元件失敗,並在工作負載繼續以完整或減少的功能繼續運作時,自動起始更正動作。 |
---|
本指南說明在雲端應用程式中處理暫時性錯誤的建議。 與遠端服務和資源通訊的所有應用程式都必須對暫時性錯誤敏感。 這特別適用於在雲端中執行的應用程式,因為環境的性質和透過因特網的連線能力,這類錯誤可能會更經常發生。 暫時性錯誤包括暫時遺失對元件和服務的網路連線、暫時無法使用服務,以及服務忙碌時發生的逾時。 這些錯誤通常是自我更正,因此,如果動作在適當延遲之後重複,可能會成功。
本文提供暫時性錯誤處理的一般指引。 如需處理暫時性錯誤的相關信息,請參閱 重試模式 ,以及當您使用 Azure 服務時,請參閱 Azure 服務的重試指引。
關鍵設計策略
暫時性錯誤可能發生在任何環境、任何平臺或操作系統上,以及任何類型的應用程式中。 針對在本機內部部署基礎結構上執行的解決方案,應用程式及其元件的效能和可用性通常會透過昂貴且通常未使用的硬體備援來維護,而元件和資源則彼此相近。 這種方法使得失敗的可能性較低,但暫時性錯誤仍可能發生,因為外部電源供應器或網路問題或災害案例等未預期事件所造成的中斷。
雲端裝載,包括私人雲端系統,可以使用共用資源、備援、自動故障轉移,以及許多商用計算節點的動態資源配置,來提供更高的整體可用性。 不過,由於雲端環境的本質,暫時性錯誤更有可能發生。 有數個原因:
雲端環境中的許多資源都會共用,而且這些資源的存取權受限於節流,以保護資源。 某些服務會在負載升至特定層級或達到最大輸送量速率時拒絕連線,以允許處理現有的要求,並維持所有用戶的服務效能。 節流有助於維護鄰近和其他使用共用資源的租使用者服務品質。
雲端環境使用大量商用硬體單位。 它們藉由將負載動態分散到多個運算單位和基礎結構元件來提供效能。 它們可藉由自動回收或取代失敗的單位來提供可靠性。 由於這種動態性質,暫時性錯誤和暫時性連線失敗可能會偶爾發生。
應用程式與其使用的資源和服務之間,通常會有更多的硬體元件,包括路由器和負載平衡器等網路基礎結構。 這個額外的基礎結構偶爾可能會造成額外的連線延遲和暫時性連線錯誤。
用戶端與伺服器之間的網路狀況可能是可變的,尤其是在通訊跨越因特網時。 即使在內部部署位置,大量流量負載也會使通訊變慢,並造成間歇性連線失敗。
暫時性錯誤可能會對應用程式的感知可用性產生重大影響,即使應用程式在所有可預見的情況下都經過徹底測試也一樣。 若要確保雲端裝載的應用程式能夠可靠地運作,您必須確保它們能夠回應下列挑戰:
應用程式必須能夠在發生錯誤時偵測錯誤,並判斷錯誤是否可能是暫時性、長期或終端機失敗。 發生錯誤時,不同的資源可能會傳回不同的回應,而且這些回應也會因作業的內容而有所不同。 例如,當應用程式從記憶體讀取時,錯誤的回應可能會與寫入記憶體時發生錯誤的回應不同。 許多資源和服務都有妥善記載的暫時性失敗合約。 不過,當無法使用這類資訊時,可能很難發現錯誤的本質,以及它是否可能是暫時性的。
如果應用程式判斷錯誤可能是暫時性的,則應用程式必須能夠重試作業。 它也需要追蹤重試作業的次數。
應用程式必須使用適當的重試策略。 此策略會指定應用程式應該重試的次數、每次嘗試之間的延遲,以及嘗試失敗后要採取的動作。 每個嘗試之間的適當次數和延遲通常很難判斷。 此策略會根據資源類型,以及資源和應用程式的目前作業條件而有所不同。
下列指導方針可協助您為應用程式設計適當的暫時性錯誤處理機制。
判斷是否有內建的重試機制
許多服務都提供包含暫時性錯誤處理機制的 SDK 或客戶端連結庫。 其使用的重試原則通常會針對目標服務的性質和需求量身打造。 或者,用於服務的 REST 介面可能會傳回可協助您確定重試是否適當以及在下次重試嘗試前要等候多久的資訊。
除非您有特定且易於理解的需求,使得採取不同的重試行為較合適,否則您應當使用現有的內建重試機制。
判斷作業是否適合重試
只有在錯誤為暫時性時,才執行重試作業(通常以錯誤的性質表示),以及重試時作業至少會成功的可能性。 嘗試無效作業的重試作業沒有意義,例如資料庫更新至不存在的專案,或對發生嚴重錯誤之服務或資源的要求。
一般而言,只有在您可以判斷執行此動作的完整效果,以及了解條件且可驗證時,才實作重試。 否則,讓呼叫端程式代碼實作重試。 請記住,從控件外部的資源和服務傳回的錯誤可能會隨著時間而演變,您可能需要重新流覽暫時性錯誤偵測邏輯。
當您建立服務或元件時,請考慮實作錯誤碼和訊息,協助客戶端判斷它們是否應該重試失敗的作業。 特別是,指出用戶端是否應該重試作業(或許傳回 isTransient 值),並在下次重試嘗試之前建議適當的延遲。 如果您建置 Web 服務,請考慮傳回服務合約中定義的自定義錯誤。 雖然一般用戶端可能無法讀取這些錯誤,但它們在建立自定義用戶端時很有用。
判斷適當的重試計數和間隔
將重試計數和間隔優化為使用案例的類型。 如果您未重試足夠的時間,應用程式就無法完成作業,而且可能會失敗。 如果您重試太多次,或嘗試間隔太短,應用程式可能會保留長時間的線程、連線和記憶體等資源,這會對應用程式的健康情況造成負面影響。
調整時間間隔的值,以及嘗試重試作業類型的數目。 例如,如果作業是用戶互動的一部分,間隔應該很短,而且應該只嘗試幾次重試。 使用這種方法,您可以避免讓使用者等候回應,而回應會保留開啟的連線,並減少其他使用者的可用性。 如果作業是長時間執行或關鍵工作流程的一部分,其中取消和重新啟動程式成本昂貴或耗時,則適合在嘗試之間等候較長的時間,然後再重試更多次。
請記住,判斷重試之間的適當間隔是設計成功策略的最困難部分。 一般策略會使用下列類型的重試間隔:
指數輪詢。 應用程式會在第一次重試之前等候一小段時間,然後以指數方式增加每次後續重試之間的時間。 例如,它可能會在 3 秒、12 秒、30 秒等之後重試作業。
累加間隔。 應用程式會在第一次重試之前等候一小段時間,然後以累加方式增加每次後續重試之間的時間。 例如,它可能會在 3 秒、7 秒、13 秒等之後重試作業。
定期間隔。 應用程式會等候每次嘗試之間的相同時間。 例如,它可能會每隔 3 秒重試一次作業。
立即重試。 有時候暫時性錯誤是短暫的,可能是因為網路封包衝突或硬體元件尖峰等事件所造成。 在此情況下,立即重試作業是適當的,因為如果錯誤在應用程式進行組合並傳送下一個要求時清除,可能會成功。 不過,不應該有一次以上的立即重試嘗試。 如果立即重試失敗,您應該切換至替代策略,例如指數輪詢或後援動作。
隨機化。 先前所列的任何重試策略都可以包含隨機化,以防止客戶端同時傳送後續重試嘗試的多個實例。 例如,一個實例可能會在 3 秒、11 秒、28 秒等之後重試作業,而另一個實例可能會在 4 秒、12 秒、26 秒等之後重試作業。 隨機化是可與其他策略結合的實用技術。
一般指導方針是針對背景作業使用指數輪詢策略,並針對互動式作業使用立即或定期的間隔重試策略。 在這兩種情況下,您應該選擇延遲和重試計數,讓所有重試嘗試的最大延遲都位於所需的端對端延遲需求內。
請考慮到所有因素的組合,這些因素促成重試作業的整體最大逾時。 這些因素包括失敗連線產生回應所需的時間(通常是由用戶端中的逾時值設定)、重試嘗試之間的延遲,以及重試次數上限。 所有這些時間的總和都可能會導致整體作業時間很長,特別是當您使用指數延遲策略時,重試間隔會在每次失敗后快速成長。 如果進程必須符合特定的服務等級協定 (SLA),則整體作業時間,包括所有逾時和延遲,都必須在 SLA 中定義的限制內。
請勿實作過於積極的重試策略。 這些策略具有太短或重試太頻繁的間隔。 它們可能會對目標資源或服務產生負面影響。 這些策略可能會防止資源或服務從其多載狀態復原,而且會繼續封鎖或拒絕要求。 此案例會導致惡性循環,其中越來越多的要求會傳送至資源或服務。 因此,其復原能力會進一步降低。
當您選擇重試間隔時,請考慮作業逾時,以避免立即啟動後續嘗試(例如,如果逾時期間類似於重試間隔)。 此外,請考慮您是否需要保留總期間 (逾時加上重試間隔) 低於特定總時間。 如果作業有異常的短或長時間逾時,逾時可能會影響等候的時間,以及重試作業的頻率。
使用例外狀況的類型及其包含的任何數據,或從服務傳回的錯誤碼和訊息,將重試次數和間隔優化。 例如,某些例外狀況或錯誤碼(例如 HTTP 代碼 503、服務無法使用、回應中具有 Retry-After 標頭)可能會指出錯誤可能持續多久,或服務失敗且不會回應任何後續嘗試。
請考慮使用寄不出的信件佇列方法,以確保所有重試嘗試都用完之後,傳入調用中的所有資訊都不會遺失。
避免反模式
在大部分情況下,請避免包含重複重試程式代碼層的實作。 請避免包含串聯重試機制的設計,或是在涉及要求階層的作業的每個階段實作重試的設計,除非您有需要執行此動作的特定需求。 在這些特殊情況下,請使用防止過多重試次數和延遲期間的原則,並確定您了解後果。 例如,假設某個元件向另一個元件提出要求,然後存取目標服務。 如果您在兩個呼叫上實作重試的計數為 3,則服務總共有 9 次重試嘗試。 許多服務和資源會實作內建的重試機制。 如果您需要在較高層級實作重試,您應該調查如何停用或修改這些機制。
永不實作無止盡的重試機制。 這樣做可能會防止資源或服務從多載情況復原,並導致節流和拒絕連線持續較長的時間。 使用有限的重試次數,或實作類似斷路器的模式,以允許服務復原。
永遠不要執行多次立即重試。
當您存取 Azure 上的服務和資源時,請避免使用一般重試間隔,特別是當您有大量重試嘗試時。 此案例中最好的方法是具有斷路器功能的指數輪詢策略。
防止相同用戶端的多個實例或不同用戶端的多個實例同時傳送重試。 如果可能發生此案例,請在重試間隔中引入隨機化。
測試重試策略和實作
盡可能廣泛地測試您的重試策略,特別是當應用程式及其使用的目標資源或服務都處於極端負載之下時。 若要在測試期間檢查行為,您可以:
將暫時性和非轉移錯誤插入服務中。 例如,傳送無效的要求或新增程序代碼,以偵測測試要求,並以不同類型的錯誤回應。
建立資源或服務的模擬,以傳回實際服務可能傳回的錯誤範圍。 涵蓋重試策略設計用來偵測的所有錯誤類型。
針對您建立和部署的自定義服務,請暫時停用或多載服務來強制發生暫時性錯誤。 (請勿嘗試在 Azure 中多載任何共用資源或共用服務。
使用攔截和修改網路流量的連結庫或解決方案,從自動化測試複寫不利的案例。 例如,測試可以新增額外的往返時間、卸除封包、修改標頭,或甚至變更要求本身的主體。 這麼做可針對暫時性錯誤和其他類型的失敗,對失敗狀況的子集進行決定性測試。
測試用戶端應用程式對暫時性錯誤的復原能力時,請使用瀏覽器的開發人員工具或測試架構模擬或封鎖網路要求的能力。
執行高負載因數和並行測試,以確保重試機制和策略在這些條件下正常運作。 這些測試也有助於確保重試對客戶端的作業沒有負面影響,或造成要求之間的交叉污染。
管理重試原則設定
重試原則是重試策略的所有元素的組合。 它會定義偵測機制,以判斷錯誤是否可能是暫時性的、要使用的間隔類型(例如一般、指數輪詢和隨機化)、實際間隔值,以及重試的次數。
在很多地方實作重試,即使是在最簡單的應用程式中,以及在更複雜的應用程式的每一層中。 請考慮使用中央點來儲存所有原則,而不是在多個位置硬式編碼每個原則的元素。 例如,將間隔和重試計數等值儲存在應用程式組態檔中、在運行時間讀取這些值,並以程序設計方式建置重試原則。 這麼做可讓您更輕鬆地管理設定,以及修改和微調值,以響應變更的需求和案例。 不過,設計系統以儲存值,而不是每次重新讀取組態檔,如果無法從組態取得值,請使用適當的預設值。
將用來在運行時間建置重試原則的值儲存在應用程式的組態系統中,讓您可以變更這些原則,而不需要重新啟動應用程式。
利用您所使用的用戶端 API 中可用的內建或預設重試策略,但僅適用於您的案例。 這些策略通常是泛型的。 在某些案例中,它們可能是您所需要的一切,但在其他案例中,它們不提供符合特定需求的完整選項。 若要判斷最適當的值,您必須執行測試,以瞭解設定如何影響您的應用程式。
記錄和追蹤暫時性和非轉移性錯誤
作為重試策略的一部分,包括例外狀況處理和其他記錄重試嘗試的檢測。 偶而發生暫時性失敗,且需要重試,且不會指出問題。 不過,一般和增加的重試次數通常是可能導致失敗或降低應用程式效能和可用性的問題指標。
將暫時性錯誤記錄為警告專案,而不是錯誤專案,讓監視系統不會將它們偵測為可能會觸發錯誤警示的應用程式錯誤。
請考慮將值儲存在記錄專案中,指出重試是由服務中的節流或其他類型的錯誤所造成,例如連線失敗,因此您可以在分析數據時加以區分。 節流錯誤數目的增加通常是應用程式中設計缺陷的指標,或需要切換到提供專用硬體的進階服務。
請考慮針對包含重試機制的作業測量和記錄整體耗用時間。 此計量是暫時性錯誤對用戶回應時間、進程延遲和應用程式使用案例效率的整體影響的良好指標。 同時記錄發生的重試次數,以便您瞭解造成響應時間的因素。
請考慮實作遙測和監視系統,以在失敗數目和速率、平均重試次數或作業成功之前經過的整體時間增加時引發警示。
管理持續失敗的作業
請考慮如何處理每次嘗試時繼續失敗的作業。 這樣的情況是不可避免的。
雖然重試策略會定義應重試作業的次數上限,但不會防止應用程式以相同次數重試作業。 例如,如果訂單處理服務失敗,併發生嚴重錯誤,使其永久退出動作,重試策略可能會偵測到連線逾時,並將它視為暫時性錯誤。 程序代碼會重試作業的指定次數,然後放棄。 不過,當另一個客戶下訂單時,即使每次作業都會失敗,仍會再次嘗試作業。
若要防止持續重試持續失敗的作業,您應該考慮實作 斷路器模式。 當您使用此模式時,如果指定時間範圍內失敗的數目超過臨界值,要求會立即以錯誤的形式傳回給呼叫端,而且不會嘗試存取失敗的資源或服務。
應用程式可以間歇性地測試服務,並在要求之間有很長的間隔,以偵測服務何時可供使用。 適當的間隔取決於作業的嚴重性和服務本質等因素。 這可能是幾分鐘到數小時之間的任何專案。 測試成功時,應用程式可以繼續正常作業,並將要求傳遞至新復原的服務。
同時,您可能能夠回復到另一個服務實例(可能位於不同的數據中心或應用程式中),使用提供相容(可能更簡單)功能的類似服務,或根據希望服務即將推出,執行一些替代作業。 例如,在佇列或數據存放區中儲存服務的要求,稍後再試一次。 或者,您可以將使用者重新導向至應用程式的替代實例、降低應用程式的效能,但仍提供可接受的功能,或只將訊息傳回給使用者,以指出應用程式目前無法使用。
優化重試實作
當您決定重試次數和原則重試間隔的值時,請考慮服務或資源上的作業是否為長時間執行或多步驟作業的一部分。 在失敗時,補償所有其他作業步驟可能很困難或昂貴。 在此情況下,只要該策略不會藉由保留或鎖定稀缺資源來封鎖其他作業,就可以接受很長的間隔和大量的重試。
請考慮重試相同的作業是否可能導致數據不一致。 如果重複執行多步驟程式的一些部分,而且作業不具等冪性,則可能會發生不一致的情況。 例如,如果重複遞增值的作業,則會產生無效的結果。 如果取用者無法偵測到重複的訊息,重複將訊息傳送至佇列的作業可能會導致訊息取用者不一致。 若要避免這些案例,請將每個步驟設計為等冪運算。 如需詳細資訊,請參閱 等冪模式。
請考慮重試的作業範圍。 例如,在包含數個作業的層級上實作重試程式代碼可能會比較容易,並在一個作業失敗時全部重試。 不過,這樣做可能會導致等冪性問題或不必要的復原作業。
如果您選擇包含數個作業的重試範圍,請在判斷重試間隔、監視作業經過的時間,以及在引發失敗警示之前,考慮所有作業的總延遲。
請考慮重試策略如何影響共用應用程式中的鄰居和其他租使用者,以及當您使用共用資源和服務時。 積極的重試原則可能會導致這些其他使用者和共用資源和服務的應用程式發生越來越多的暫時性錯誤。 同樣地,您的應用程式可能會受到資源與服務其他使用者所實作的重試原則所影響。 對於業務關鍵性應用程式,您可能想要使用未共用的進階服務。 這樣做可讓您更充分地控制這些資源和服務的負載和後續節流,這有助於證明額外的成本。
注意
如需取捨和風險的進一步指引,請參閱 重試模式文章中的問題和考慮 。
Azure 便利化
大部分的 Azure 服務和用戶端 SDK 都提供重試機制。 不過,這些機制不同,因為每個服務都有不同的特性和需求,而且每個重試機制都會調整為特定服務。 本節摘要說明一些常用 Azure 服務的重試機制功能。
服務 | 重試功能 | 原則設定 | 範圍 | 遙測功能 |
---|---|---|---|---|
Microsoft Entra ID | Microsoft驗證連結庫中的原生 (MSAL) | 內嵌至 MSAL 連結庫 | 內部 | 無 |
Azure Cosmos DB | 服務中的原生 | 無法設定 | 全球 | TraceSource |
Azure Data Lake 儲存體 | 用戶端中的原生 | 無法設定 | 個別作業 | 無 |
Azure 事件中樞 | 用戶端中的原生 | 程式設計 | 用戶端 | 無 |
Azure IoT 中樞 | 用戶端 SDK 中的原生 | 程式設計 | 用戶端 | 無 |
Azure Cache for Redis | 用戶端中的原生 | 程式設計 | 用戶端 | TextWriter |
Azure 認知搜尋 | 用戶端中的原生 | 程式設計 | 用戶端 | ETW 或自定義 |
Azure 服務匯流排 | 用戶端中的原生 | 程式設計 | NamespaceManager、MessagingFactory 和用戶端 | ETW |
Azure Service Fabric | 用戶端中的原生 | 程式設計 | 用戶端 | 無 |
具有 ADO.NET 的 Azure SQL 資料庫 | Polly | 宣告式和程序設計 | 單一語句或程式代碼區塊 | 自訂 |
使用 Entity Framework SQL 資料庫 | 用戶端中的原生 | 程式設計 | 每個 AppDomain 的全域 | 無 |
使用 Entity Framework Core SQL 資料庫 | 用戶端中的原生 | 程式設計 | 每個 AppDomain 的全域 | 無 |
Azure 儲存體 | 用戶端中的原生 | 程式設計 | 用戶端和個別作業 | TraceSource |
注意
對於大部分的 Azure 內建重試機制,目前無法針對不同類型的錯誤或例外狀況套用不同的重試原則。 您應該設定提供最佳平均效能和可用性的原則。 微調原則的其中一種方法是分析記錄檔,以判斷發生的暫時性錯誤類型。
範例
如需使用本文所討論之許多模式的範例,請參閱 適用於 .NET 的可靠 Web 應用程式模式。 GitHub 也有 參考實 作。
相關連結
可靠性檢查清單
請參閱一組完整的建議。