共用方式為


暫時性錯誤處理

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

本文提供暫時性錯誤處理的一般指引。

為什麼在雲端發生暫時性錯誤?

暫時性錯誤可能發生在任何環境、任何平臺或作系統上,以及任何類型的應用程式中。 針對在本機內部部署基礎結構上執行的解決方案,應用程式及其元件的效能和可用性通常會透過昂貴且通常未使用的硬體備援來維護,而元件和資源則彼此相近。 這種方法降低了失敗的可能性,但暫時性錯誤仍可能發生,同樣地,由於外部電源供應或網路問題等未預期事件,或其他災害情境造成的中斷也可能發生。

雲端裝載,包括私人雲端系統,可以使用共用資源、備援、自動故障轉移,以及許多商用計算節點的動態資源配置,來提供更高的整體可用性。 不過,由於雲端環境的本質,暫時性錯誤更有可能發生。 有數個原因:

  • 雲端環境中的許多資源都會共用,而且這些資源的存取權受限於節流,以保護資源。 某些服務會在負載升至特定層級或達到最大輸送量速率時拒絕連線,以允許處理現有的要求,並維持所有用戶的服務效能。 節流有助於維護鄰近和其他使用共用資源的租使用者服務品質。

  • 雲端環境使用大量商用硬體單位。 它們藉由將負載動態分散到多個運算單位和基礎結構元件來提供效能。 它們可藉由自動回收或取代失敗的單位來提供可靠性。 由於這種動態性質,暫時性錯誤和暫時性連線失敗可能會偶爾發生。

  • 應用程式與其使用的資源和服務之間,通常會有更多的硬體元件,包括路由器和負載平衡器等網路基礎結構。 這個額外的基礎結構偶爾可能會造成額外的連線延遲和暫時性連線錯誤。

  • 用戶端與伺服器之間的網路狀況可能是可變的,尤其是在通訊跨越因特網時。 即使在內部部署位置,大量流量負載也會使通訊變慢,並造成間歇性連線失敗。

挑戰

暫時性錯誤可能會對應用程式的感知可用性產生重大影響,即使應用程式在所有可預見的情況下都經過徹底測試也一樣。 若要確保雲端裝載的應用程式能夠可靠地運作,您必須確保它們能夠回應下列挑戰:

  • 應用程式必須能夠在發生錯誤時偵測錯誤,並判斷錯誤是否可能是暫時性、長期或終端機失敗。 發生錯誤時,不同的資源可能會傳回不同的回應,而且這些回應也會因作業的內容而有所不同。 例如,當應用程式從記憶體讀取時,錯誤的回應可能會與寫入記憶體時發生錯誤的回應不同。 許多資源和服務都有妥善記載的暫時性故障協議。 不過,當無法使用這類資訊時,可能很難發現錯誤的本質,以及它是否可能是暫時性的。

  • 如果應用程式判斷錯誤可能是暫時性的,則應用程式必須能夠重試作業。 它也需要追蹤重試作業的次數。

  • 應用程式必須使用適當的重試策略。 此策略會指定應用程式應該重試的次數、每次嘗試之間的延遲,以及嘗試失敗后要採取的動作。 每個嘗試之間的適當次數和延遲通常很難判斷。 此策略會根據資源類型和資源的目前作業條件和應用程式而有所不同。

一般指導方針

下列指導方針可協助您為應用程式設計適當的暫時性錯誤處理機制。

判斷是否有內建的重試機制

  • 許多服務都提供包含暫時性錯誤處理機制的 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 中可用的內建或預設重試策略,但僅適用於您的案例。 這些策略通常是泛型的。 在某些案例中,它們可能是您所需要的一切,但在其他案例中,它們不提供符合特定需求的完整選項。 若要判斷最適當的值,您必須執行測試,以瞭解設定如何影響您的應用程式。

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

  • 作為重試策略的一部分,應包括例外處理和其他記錄重試嘗試的監控工具。 偶爾發生的暫時性故障是正常的,需要重試,但這並不代表問題。 不過,一般和增加的重試次數通常是可能導致失敗或降低應用程式效能和可用性的問題指標。

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

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

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

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

管理持續失敗的作業

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

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

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

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

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

其他考慮

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

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

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

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

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