處理暫時性錯誤的建議

適用于此 Azure Well-Architected Framework 可靠性檢查清單建議:

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 標頭) 可能會指出錯誤可能持續多久,或服務失敗,且不會回應任何後續嘗試。

  • 請考慮使用寄不出的信件佇列方法,以確保所有來自傳入調用的資訊在重試嘗試都用完之後都不會遺失。

避免反模式

  • 在大部分情況下,請避免實作包含重複的重試程式碼層。 請避免包含串聯重試機制的設計,或在涉及要求階層的作業的每個階段實作重試的設計,除非您有需要這麼做的特定需求。 在這些例外狀況下,請使用原則避免過多的重試次數和延遲期間過長,並確定您瞭解後果。 例如,假設某個元件向另一個元件提出要求,然後存取目標服務。 如果您在兩次呼叫上實作三次重試,則服務總共有九次重試嘗試。 許多服務和資源會實作內建重試機制。 如果您需要在較高層級實作重試,您應該調查如何停用或修改這些機制。

  • 永遠不要實作無盡的重試機制。 這樣做可能會導致資源或服務無法從多載狀況復原,並導致節流和拒絕連線持續較長的時間。 使用有限的重試次數,或實作 類似斷路器 的模式,以允許服務復原。

  • 永遠不要執行立即重試一次以上。

  • 當您存取 Azure 上的服務和資源時,請避免使用定期重試間隔,特別是當您嘗試大量重試時。 此案例的最佳方法是具有斷路器功能的指數輪詢策略。

  • 防止相同用戶端的多個實例或不同用戶端的多個實例同時傳送重試。 如果可能發生此案例,請在重試間隔中引入隨機化。

測試重試策略和實作

  • 盡可能在一組廣泛的情況下完全測試您的重試策略,特別是當應用程式和其使用的目標資源或服務都處於極端負載時。 若要檢查測試期間的行為,您可以:

    • 將暫時性與非暫時性錯誤插入服務中。 例如,傳送無效要求,或新增程式碼來偵測不同錯誤類型的測試要求與回應。

    • 建立資源或服務的模擬,以傳回實際服務可能傳回的錯誤範圍。 涵蓋重試策略設計來偵測的所有錯誤類型。

    • 針對您建立和部署的自訂服務,請暫時停用或多載服務,強制發生暫時性錯誤。 (請勿嘗試多載 Azure.) 中的任何共用資源或共用服務

    • 使用攔截和修改網路流量的程式庫或解決方案,從自動化測試複寫不偏好的案例。 例如,測試可以新增額外的往返時間、卸載封包、修改標頭,或甚至變更要求本身的主體。 這麼做可針對暫時性錯誤和其他失敗類型,對失敗狀況子集進行決定性測試。

    • 測試用戶端應用程式對暫時性錯誤的復原能力時,請使用瀏覽器的開發人員工具或測試架構模擬或封鎖網路要求的能力。

    • 執行高負載因數和並行測試,以確保重試機制和策略在這些情況下正常運作。 這些測試也有助於確保重試不會對用戶端的作業造成負面影響,或造成要求之間的交叉干擾。

管理重試原則設定

  • 重試原則是重試策略的所有元素的組合。 它會定義偵測機制,判斷錯誤是否可能是暫時性的、 (要使用的間隔類型,例如一般、指數輪詢和隨機化) 、實際間隔值,以及重試的次數。

  • 在許多位置實作重試,即使是在最簡單的應用程式中,以及在更複雜的應用程式的每一層中。 請考慮使用中央點來儲存所有原則,而不是在多個位置硬式編碼每個原則的元素。 例如,將間隔和重試計數等值儲存在應用程式組態檔中、在執行時間讀取這些值,並以程式設計方式建置重試原則。 這麼做可讓您更輕鬆地管理設定,以及修改和微調值,以回應變更的需求和案例。 不過,請設計系統來儲存值,而不是每次重新讀取組態檔,如果無法從組態取得值,請使用適當的預設值。

  • 在應用程式的組態系統中儲存用來在執行時間建置重試原則的值,讓您可以變更它們,而不需要重新開機應用程式。

  • 利用您所使用的用戶端 API 中可用的內建或預設重試策略,但僅適用于您的案例。 這些策略通常是一般。 在某些情況下,它們可能都是您需要的,但在其他案例中,它們不會提供完整的選項範圍,以符合您的特定需求。 若要判斷最適當的值,您必須執行測試,以瞭解設定如何影響您的應用程式。

記錄和追蹤暫時性和非轉譯錯誤

  • 作為重試策略的一部分,包括例外狀況處理和其他記錄重試嘗試的檢測。 偶爾會發生暫時性失敗,且重試不會指出問題。 不過,一般和增加的重試次數通常是可能導致失敗或降低應用程式效能和可用性的問題指標。

  • 將暫時性錯誤記錄為警告專案,而不是錯誤專案,讓監視系統不會將其偵測為可能會觸發 false 警示的應用程式錯誤。

  • 請考慮將值儲存在記錄專案中,指出重試是由服務中的節流或其他類型的錯誤所造成,例如連線失敗,因此您可以在分析資料期間加以區分。 節流錯誤數目的增加,這通常代表應用程式的設計有瑕疵,或是需要改用可提供專用硬體的高階服務。

  • 請考慮測量和記錄包含重試機制之作業的整體耗用時間。 此計量是暫時性錯誤對使用者回應時間、進程延遲和應用程式使用案例效率的整體影響良好指標。 同時記錄發生的重試次數,以便您瞭解造成回應時間的因素。

  • 請考慮實作遙測和監視系統,以在失敗數目和速率、重試的平均次數,或作業成功之前經過的整體時間增加時引發警示。

管理持續失敗的作業

  • 請考慮如何處理每次嘗試時繼續失敗的作業。 這類情況是不可避免的。

    • 雖然重試策略會定義應重試作業的最大次數,但不會防止應用程式以相同次數重試重複作業。 例如,如果訂單處理服務失敗,併發生嚴重錯誤,使其無法永久運作,重試策略可能會偵測到連線逾時,並將它視為暫時性錯誤。 程式碼會重試作業的指定次數,然後放棄。 不過,當另一位客戶下訂單時,即使每次作業都會失敗,仍會再次嘗試作業。

    • 若要防止持續失敗的作業進行持續重試,您應該考慮實作 斷路器模式。 當您使用此模式時,如果指定時間範圍內失敗的數目超過臨界值,要求會立即以錯誤的形式返回呼叫端,而且不會嘗試存取失敗的資源或服務。

    • 應用程式會定期測試服務,並間歇性地偵測服務何時可供使用,且要求之間的間隔很長。 適當的間隔取決於作業重要性和服務本質等因素。 它可能是幾分鐘到數小時之間的任何專案。 當測試成功時,應用程式可以繼續正常作業,並將要求傳遞至新復原的服務。

    • 同時,您可能能夠回到另一個服務實例, (可能位於不同的資料中心或應用程式) 、使用提供相容 (可能更簡單) 功能的類似服務,或根據服務即將可供使用的方式執行一些替代作業。 例如,可能適合將服務的要求儲存在佇列或資料存放區中,稍後再重試。 或者,您可以將使用者重新導向至應用程式的替代實例、降低應用程式的效能,但仍提供可接受的功能,或只將訊息傳回給使用者,以指出應用程式目前無法使用。

其他考量

  • 當您決定重試次數和原則重試間隔的值時,請考慮服務或資源的作業是長時間執行或多步驟作業的一部分。 在失敗時,補償所有其他作業步驟可能很困難或昂貴。 在此情況下,只要該策略不會藉由保留或鎖定資源來封鎖其他作業,就可以接受非常長的間隔和大量重試。

  • 請考慮重試相同的作業是否會導致資料不一致。 如果重複執行多個步驟程式的部分,而且作業不具等冪性,可能會發生不一致的情況。 例如,如果遞增值的作業重複,則會產生不正確結果。 重複將訊息傳送至佇列的作業,如果取用者無法偵測到重複的訊息,訊息取用者可能會導致訊息取用者不一致。 若要避免這些案例,請將每個步驟設計為等冪運算。 如需詳細資訊,請參閱 等冪模式

  • 請考慮重試的作業範圍。 例如,在包含數個作業的層級實作重試程式碼可能會比較容易,並在一個作業失敗時全部重試。 不過,這樣做可能會導致等冪性問題或不必要的復原作業。

  • 如果您選擇包含數個作業的重試範圍,請在您決定重試間隔、監視作業經過的時間,以及在引發失敗警示之前,考慮所有作業的總延遲。

  • 請考慮您的重試策略如何影響共用應用程式中的鄰近和其他租使用者,以及當您使用共用資源和服務時。 積極重試原則會導致其他的使用者,以及共用資源與服務的應用程式,發生越來越多的暫時性錯誤。 同樣地,您的應用程式可能會受到資源與服務其他使用者所實作的重試原則所影響。 對於業務關鍵性應用程式,您可能想要使用未共用的進階服務。 這麼做可讓您更充分地控制這些資源和服務的負載和後續節流,這有助於證明額外的成本。

注意

如需取捨和風險的進一步指引,請參閱重試模式文章中的 問題和考慮

Azure 設施

大部分的 Azure 服務和用戶端 SDK 都提供重試機制。 不過,這些機制不同,因為每個服務都有不同的特性和需求,而且每個重試機制都會調整為特定服務。 本節摘要說明一些常用 Azure 服務的重試機制功能。

服務 重試功能 原則設定 範圍 遙測功能
Microsoft Entra 識別碼 Microsoft 驗證程式庫中的原生 (MSAL) 內嵌至 MSAL 程式庫 內部
Azure Cosmos DB 服務中的原生 無法設定 全球 TraceSource
Azure Data Lake Storage 用戶端中的原生 無法設定 個別作業
Azure 事件中樞 用戶端中的原生 程式設計 用戶端
Azure IoT 中樞 用戶端 SDK 中的原生 程式設計 用戶端
Azure Cache for Redis 用戶端中的原生 程式設計 用戶端 TextWriter
Azure 認知搜尋 用戶端中的原生 程式設計 用戶端 ETW 或自訂
Azure 服務匯流排 用戶端中的原生 程式設計 NamespaceManager、MessagingFactory 和用戶端 ETW
Azure Service Fabric 用戶端中的原生 程式設計 用戶端
具有 ADO.NET 的 Azure SQL 資料庫 Polly 宣告與程式設計 單一陳述式或程式碼區塊 Custom
使用 Entity Framework 的 SQL Database 用戶端中的原生 程式設計 每個 AppDomain 全域
使用 Entity Framework Core 的 SQL Database 用戶端中的原生 程式設計 每個 AppDomain 全域
Azure 儲存體 用戶端中的原生 程式設計 用戶端與個別作業 TraceSource

注意

對於大部分的 Azure 內建重試機制,目前無法針對不同類型的錯誤或例外狀況套用不同的重試原則。 您應該設定一個原則,提供最佳的平均效能和可用性。 微調原則的其中一種方式是分析記錄檔,以判斷發生的暫時性錯誤類型。

範例

如需使用本文所討論之許多模式的範例,請參閱 .NET 的可靠 Web 應用程式模式 。 GitHub 上也有 參考實作

可靠性檢查清單

請參閱一組完整的建議。