天天看點

重試暫時性故障處理設計-常用的架構設計原則

與遠端服務和資源通信的所有應用程式必須對暫時性故障敏感。 對于雲中運作的應用程式尤其如此,因為其環境的性質與通過 Internet 建立連接配接的特點,意味着更容易遇到這種類型的故障。 暫時性故障包括元件和服務瞬間斷開網絡連接配接、服務暫時不可用,或者當服務繁忙時出現逾時。 這些故障通常可自我糾正,如果在适當的延遲後重複操作,則可能會成功。

為什麼雲中會出現暫時性故障?

任何環境、任何平台或作業系統以及任何類型的應用程式都會發生暫時性故障。 在本地基礎結構上運作的解決方案中,應用程式及其元件的性能和可用性通常是通過昂貴且經常未充分利用的硬體備援來維護的,元件和資源彼此接近。 雖然這種方法會降低故障出現機率,但仍可能導緻暫時性故障,甚至可能會因出現無法預見的事件(例如外部電源或網絡問題或其他災難方案)而中斷。

雲托管(包括私有雲系統)可以通過使用共享資源、備援、自動故障轉移以及在多個商用計算節點之間動态配置設定資源,提供更高的總體可用性。 但是這些環境的性質意味着更可能發生暫時性故障。 原因包括:

  • 雲環境中的許多資源是共享的,為了保護這些資源,會限制對這些資源的通路。 某些服務在負載上升到特定級别時,或到達吞吐量比率的上限時,會拒絕連接配接以便處理現有的請求,并為所有使用者維持服務性能。 限制有助于為共享資源的鄰居與其他租戶維持服務品質。
  • 雲環境是使用大量商用硬體單元建構而成的。 雲環境将負載動态分散到多個計算單元和基礎結構元件上以提供性能,并通過自動回收或更換故障單元來提供可靠性。 這種動态性意味着可能偶爾會發生暫時性故障和暫時連接配接失敗。
  • 在應用程式與資源及其使用的服務之間,通常有多個硬體元件,包括網絡基礎結構,例如路由器和負載均衡器。 這個附加的基礎結構偶爾會導緻額外的連接配接延遲與暫時性連接配接故障。
  • 用戶端與伺服器之間的網絡狀況會不時改變,尤其是通過 Internet 通信時。 即使在本地位置,負載繁重的流量也可能會導緻通信變慢并導緻間歇性的連接配接故障。

挑戰

暫時性故障對應用程式的可感覺可用性有重大影響,即使已在所有可預見的情況下對其進行了全面的測試。 若要確定雲托管的應用程式可靠運作,應用程式必須能夠應對以下挑戰:

  • 應用程式必須能夠檢測到故障的發生,并确定這些故障可能是暫時性的、持久性的還是終端故障。 發生故障時,不同的資源可能傳回不同的響應,這些響應可能會根據操作上下文而有所不同,例如,針對從存儲讀取時所發生錯誤傳回的響應,與針對寫入存儲時所發生錯誤傳回的響應不同。 許多資源和服務都妥善制定了暫時性故障的合約。 但是,若不提供此類資訊,則很難發現故障的性質,以及故障是否是暫時性的。
  • 如果确定故障可能是暫時性的,應用程式必須能夠重試操作,并跟蹤操作重試的次數。
  • 應用程式必須使用适當的重試政策。 此政策指定應用程式應該重試的次數、每兩次嘗試的延遲時間,以及嘗試失敗後執行的操作。 适當的嘗試次數以及每兩次嘗試的延遲時間通常難以确定,會根據資源類型以及應用程式本身的目前操作條件而有所不同。

一般指南

以下指南将幫助你為應用程式設計适當的暫時性故障處理機制:

  • 确定是否存在内置的重試機制:
    • 許多外部服務提供SDK或包含暫時性故障處理機制的用戶端庫。 服務使用的重試政策通常是根據目标服務的性質和要求定制的。 或者對于确定重試是否适當,以及在下一次嘗試重試之前要等待多長時間方面,服務的REST接口可能會傳回有用的資訊。
    • 使用内置的重試機制(如果可用),除非你具有更合适的、更合适的重試行為的特定易懂的要求。
  • 确定操作是否适合重試:
    • 隻在故障是暫時性(通常可由故障的性質來判斷),以及在重新嘗試後操作至少有一些成功的可能性時,才應重試操作。 意義操作中不存在訓示操作無效的操作,例如對不存在的項進行資料庫更新,或請求出現嚴重錯誤的服務或資源的請求。
    • 一般而言,隻有在能夠确定操作的整個影響,且狀況已獲得充分了解并可驗證時,才實施重試。 否則,應該由調用代碼來實施重試。 請記住,從無法控制的資源與服務傳回的錯誤可能會随着時間而演進,可能需要重新通路暫時性故障檢測邏輯。
    • 建立服務或元件時,請考慮實施錯誤代碼和消息,以幫助用戶端确定是否應重試失敗的操作。 具體而言,指明用戶端是否應重試操作(也許是通過傳回 isTransient 值),并建議下一次重試之前的适當延遲。 如果要建構 Web 服務,請考慮傳回服務合約中定義的自定義錯誤。 即使一般的用戶端可能無法讀取這些錯誤,但在建構自定義用戶端時,自定義錯誤很有幫助。
  • 确定适當的重試計數與間隔:
    • 請務必優化重試計數和用例類型的間隔。 如果重試次數不足,應用程式将無法完成操作,并且可能會失敗。 如果重試次數太多或間隔太短,應用程式可能會長期保留資源(例如線程、連接配接和記憶體),這會對應用程式的運作狀況造成不利影響。
    • 适當的時間間隔與重試次數值取決于正在嘗試的操作類型。 例如,如果操作是使用者互動的一部分,則間隔應該較短,且隻需重試幾次,以避免讓使用者等待響應(這會讓連接配接保持打開,并可能會降低其他使用者的可用性)。 如果操作是長時間運作的工作流或關鍵工作流的一部分,在這種情況下,取消和重新開機程序會消耗大量資源或耗時,是以最好等待兩次嘗試,然後再重試。
    • 确定适當的重試間隔是設計一個成功的政策時最困難的部分。 典型的政策會使用以下類型的重試間隔:
      • 指數退讓。 應用程式在第一次重試之前短暫地等待,每個後續重試的間隔時間呈指數增加。 例如,在 3 秒、12 秒、30 秒後重試操作。
      • 增量間隔。 應用程式在第一次重試之前短暫地等待,每個後續重試的間隔時間增量遞增。 例如,在 3 秒、7 秒、13 秒後重試操作。
      • 固定間隔。 應用程式每次嘗試的間隔時間相同。 例如,固定每 3 秒重試操作。
      • 立即重試。 有時暫時性故障很短暫,原因可能是網絡資料包沖突或硬體元件中的峰值。 在此情況下,适合立即重試操作,因為如果故障在操作讓應用程式組合并發送下一個請求時已清除,則操作可能會成功。 但是,如果立即重試失敗,應切換為備用政策,例如指數回退或回退操作,而不應超過一次立即重試次數。
      • 随機化。 任何上述重試政策都可包含随機化,以防止用戶端的多個執行個體同時發送後續重試請求。 例如,一個執行個體可能會在3秒、11秒、28秒後重試操作,而另一個執行個體可以在4秒、12秒、26秒後重試該操作。 随機化是有用的技術,可配合其他政策使用。
    • 一般指導原則是,為背景操作使用指數退讓政策,為互動式操作使用立即或固定間隔重試政策。 在上述兩種情況下,應該選擇延遲與重試計數,使所有重試的延遲上限都在所需的端到端延遲要求範圍内。
    • 請考慮到所有會對重試操作的整體逾時上限造成影響的因素組合。 這些因素包括失敗連接配接生成響應所花費的時間(通常根據用戶端的逾時值設定),以及重試之間的延遲和重試次數上限。 所有這些時間的總和會導緻整體操作時間較長,尤其是在使用指數延遲政策時,在這種情況下,每次發生故障後重試的間隔會迅速增長。 如果某個程序必須滿足 (SLA) 的特定服務級别協定,則整體操作時間(包括所有的逾時和延遲)必須在 SLA 中定義的限制範圍内。
    • 過于頻繁的重試政策(其間隔太短或重試過于頻繁)會對目标資源或服務造成不利影響。 這可能會造成資源或服務無法從過載狀态恢複,并且會繼續阻止或拒絕請求。 這會造成惡性循環,越來越多的請求将發送到資源或服務,因而造成其恢複能力進一步降低。
    • 選擇重試間隔時請考慮操作的逾時,以避免立即啟動後續嘗試(例如當逾時期間與重試間隔類似時)。 還應考慮是否需要讓可能期間的總和(逾時值加重試間隔)短于特定的時間總和。 逾時設定非常短或非常長的的操作可能會影響等待時間,以及重試操作的頻率。
    • 使用異常類型及其包含的任何資料,或者使用從服務傳回的錯誤代碼與消息,來優化重試的間隔和次數。 例如,某些異常或錯誤代碼(如 HTTP 代碼 503 - 服務不可用,以及響應中的 Retry-After 标頭)會訓示錯誤可能持續的時間,或服務失敗且不會響應任何後續嘗試。
  • 避免反模式:
    • 在絕大多數情況下,應該避免使用包含重複重試代碼層的實作。 避免使用包括級聯重試機制的設計,或避免使用在涉及請求層次結構的操作的每個階段實施重試的設計,除非有特定的要求。 在這些例外的情況下下,請使用政策避免過多的重試次數和延遲期間過長,并確定了解後果。 例如,如果某個元件對另一個元件送出請求,後者再通路目标服務,并且要對這兩個調用各實施重試三次,則總共會對該服務重試九次。 許多服務和資源實施内置重試機制,如果需要在較進階别實施重試,應調查如何禁用或修改此設定。
    • 切勿實施永不結束的重試機制。 這很可能會導緻資源或服務無法從過載情況下恢複,并造成限制與遭到拒絕的連接配接持續更長時間。 使用有限的重試次數或使用斷路器等模式,使服務可以恢複。
    • 切勿多次執行立即重試。
    • 避免使用固定重試間隔,尤其是在通路公有雲計算環境中的服務與資源期間要重試很多次時。 此情況下的最佳方法是指數退讓政策以及斷路功能。
    • 防止同一個用戶端有多個執行個體,或不同用戶端有多個執行個體同時發送重試請求。 如果這有可能發生,請在重試間隔中引入随機化。
  • 測試重試政策與實施:
    • 確定在盡可能廣泛的條件下全面測試重試政策實施,尤其是當實施使用的應用程式與目标資源或服務要承受極端負載時。 要檢查測試期間的行為,可以:
      • 将暫時性故障和非暫時605故障注入服務中。 例如,發送無效請求或添加代碼用于檢測包含不同錯誤類型的測試請求與響應。
      • 建立資源或服務模型,用于傳回真實服務可能傳回的錯誤範圍。 確定覆寫重試政策旨在檢測的所有錯誤類型。
      • 通過暫時禁用或重載服務中的任何共享資源或共享服務。
      • 對于基于 HTTP 的 API,請考慮在自動化測試中使用 FiddlerCore 庫來更改 HTTP 請求的結果,方法是增加額外的往返時間或更改響應(例如 HTTP 狀态代碼、标頭、正文或其他因素)。 這樣,便可以确定性地測試一部分故障狀況,無論是暫時性故障還是其他類型的故障。 有關詳細資訊,請參閱 FiddlerCore。 
      • 執行高負載因子和并發測試,確定重試機制與政策在這些條件下能正常工作,且不會對用戶端操作造成不良的影響或導緻請求之間交叉污染。
  • 管理重試政策配置:
    • 重試政策 是所有重試政策元素的組合。 它定義了能确定故障是否可能是暫時性的檢測機制、使用的間隔類型(例如固定、指數退讓及随機化)、實際間隔值,以及重試次數。
    • 必須在應用程式中的多個位置實施重試,即使是最簡單的應用程式也是如此,至于較複雜的應用程式,則必須每一層都實施。 考慮使用集中點存儲所有政策,而不是将每個政策的元素寫死在多個位置。 例如,在應用程式配置檔案中存儲間隔和重試計數等值、在運作時讀取這些值,并以程式設計方式建構重試政策。 這樣可以更輕松地管理設定,以及修改和優化值,以應對不斷變化的要求和方案。 但是,請将系統設計為存儲值而不是每次都要重新讀取配置檔案,并確定在無法從配置中擷取值時使用适當的預設值。
    • 考慮在公有雲服務應用程式中存儲那些不變值,用于在服務配置檔案中建構運作時的重試政策,使這些值不需要重新啟動應用程式即可更改。
    • 利用所用用戶端 API 中的内置或預設重試政策,但前提是這些政策适合方案。 這些政策通常是通用的。 在某些狀況下,這些政策可能都是必需的,但在某些狀況下,它們可能無法提供完整的選項範圍來滿足特定的要求。 必須通過測試來了解設定如何影響應用程式,以确定最合适的值。
  • 記錄和跟蹤暫時性故障和非暫時605故障:
    • 在重試政策中包含異常處理,以及其他用于記錄重試時間的工具。 盡管偶爾發生暫時性故障和重試是預期的,但并不表示存在問題,但定期重試的重試次數通常是可能導緻故障的問題的訓示器,或者目前正在降低應用程式的性能和可用性。
    • 将暫時性故障記錄為“警告”項,而不是“錯誤”項,以便監視系統不會将其檢測為應用程式錯誤而觸發誤報。
    • 考慮将值存儲在日志項中,用于訓示重試是由服務中的限制所造成,或是由其他類型的故障(例如連接配接失敗)造成,使你能夠在分析資料期間進行區分。 限制錯誤數目的增加通常表示應用程式的設計有瑕疵,或者需要改用可提供專用硬體的進階服務。
    • 考慮測量和記錄包含重試機制的操作所需的總體時間。 這是暫時性故障對使用者響應時間、程序延遲以及應用程式用例效率造成的整體影響的良好名額。 此外,還要記錄發生重試的次數,以了解影響響應時間的因素。
    • 考慮實施遙測和監視系統,當故障次數與比率、重試平均次數或操作成功所需的整體時間增加時,該系統會引發警報。
  • 管理不斷失敗的操作:
    • 如果每次嘗試後操作仍然失敗,則必須考慮如何處理這種情況:
      • 盡管重試政策會定義操作應重試次數的上限,但它不會防止應用程式使用與重試次數相同的次數一再重複操作。 例如,如果訂單處理服務因為嚴重錯誤而失敗且永久失效,則重試政策會檢測連接配接逾時,并将它視為暫時性故障。 代碼将按指定的次數重試操作,然後放棄。 但是,當另一位客戶下單時,會再次嘗試該操作,即使該操作每次肯定都會失敗。
      • 為防止不斷重試連續失敗的操作,請考慮實施斷路器模式。 在此模式中,如果在指定的時段内失敗次數超過門檻值,則會立即将請求傳回給調用方,并将失敗視為故障,而不會嘗試通路失敗的資源或服務。
      • 應用程式可以按間歇定期測試服務,并在請求之間進行較長的間隔,以檢測其何時變為可用。 适當的間隔取決于方案,例如操作的重要性和服務的性質,可能是數分鐘到數個小時。 測試成功時,應用程式将恢複正常操作,并将請求傳遞給剛剛恢複的服務。
      • 同時,可以故障回複到服務的另一個執行個體(也許在不同的資料中心或應用程式中)、使用提供相容(也許是更簡單)功能的類似服務,或執行某些替代操作,以期該服務很快可供使用。 例如,有時适合将服務的請求存儲在隊列或資料存儲中,供以後重複使用。 也可以将使用者重定向到應用程式的其他執行個體、使應用程式性能降級但仍可提供可接受的功能,或者隻是将消息傳回給使用者,指出應用程式暫時不可用。
  • 其他注意事項
    • 在決定重試次數的值和政策的重試間隔時,請考慮服務或資源的操作是否是長時間運作的或多步驟的操作的一部分。 當某個操作步驟失敗時,補償其他所有操作步驟可能會很困難或代價非凡。 在此情況下,可以接受很長的間隔及大量的重試次數,前提是不會因為保留或鎖定稀缺資源而阻止其他操作。
    • 考慮重試相同的操作是否會導緻資料不一緻。 如果多步驟程序的某些部分重複,并且操作不是幂等的,則可能會導緻不一緻。 例如,遞增值的操作如果重複,則會生成無效的結果。 重複執行将消息發送到隊列的操作如果無法檢測到重複消息,則可能會對消息使用者造成不一緻情況。 要避免此問題,請確定将每個步驟設計成幂等操作。 有關幂等性的詳細資訊,請參閱 幂等性模式。
    • 考慮重試操作的範圍。 例如,可以更輕松地在包含數個操作的級别實施重試代碼,如果其中一個操作失敗,則重試所有操作。 但是,這可能會導緻幂等性問題或不必要的復原操作。
    • 如果選擇的重試範圍包含多個操作,在确定重試間隔時、監視花費時間時,以及因失敗而引發警報之前,請考慮所有操作的延遲總和。
    • 考慮重試政策如何影響共享應用程式中的鄰居或其他租戶,以及何時使用共享資源與服務。 積極重試政策會導緻其他使用者以及共享資源與服務的應用程式發生越來越多的暫時性故障。 同樣地,應用程式可能會受到資源與服務的其他使用者所實施的重試政策的影響。 對于任務關鍵型應用程式,可以決定使用非共享的進階服務。 這樣就可以更好地控制這些資源與服務的負載與後續限制,進而找到提高成本的理由。

繼續閱讀