暫時性錯誤處理

與遠端服務和資源通訊的所有應用程式都必須對暫時性錯誤敏感。 這特別適用于在雲端中執行的應用程式,因為環境的性質和透過網際網路的連線能力,這類錯誤可能會更經常發生。 暫時性錯誤包括暫時遺失對元件和服務的網路連線、暫時無法使用服務,以及服務忙碌時發生的逾時。 這些錯誤通常是自我更正,因此,如果動作在適當延遲之後重複,可能會成功。

本文提供暫時性錯誤處理的一般指引。 如需使用 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 上的服務和資源時,請避免使用一般重試間隔,特別是當您有大量重試嘗試時。 此案例中最好的方法是具有斷路器功能的指數輪詢策略。

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

測試重試策略和實作

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

    • 將暫時性和非轉移錯誤插入服務中。 例如,傳送不正確要求或新增程式碼,以偵測測試要求,並以不同類型的錯誤回應。 如需使用 TestApi 的範例,請參閱 使用 TestApi 進行錯誤插入測試和 TestApi 簡介 – 第 5 部分:Managed 程式碼錯誤插入 API 。

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

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

    • 針對 HTTP 型 API,請考慮在自動化測試中使用程式庫來變更 HTTP 要求的結果,方法是新增額外的往返時間或變更回應(例如 HTTP 狀態碼、標頭、本文或其他因素)。 這麼做可針對暫時性錯誤和其他類型的失敗,對失敗狀況的子集進行決定性測試。

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

管理重試原則設定

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

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

  • 在 Azure 雲端服務 應用程式中,請考慮將用來在執行時間建立重試原則的值儲存在服務組態檔中,讓您可以變更這些原則,而不需要重新開機應用程式。

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

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

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

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

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

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

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

管理持續失敗的作業

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

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

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

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

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

其他考量

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

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

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

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

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