(十八)—— 重試模式

2018-02-24 15:44 更新

云計(jì)算設(shè)計(jì)模式(十八)——重試模式

啟用應(yīng)用程序來處理預(yù)期的,暫時(shí)的失敗時(shí),它會(huì)嘗試連接到由透明的重試操作了以前失敗的期望,失敗的原因是瞬時(shí)的服務(wù)或網(wǎng)絡(luò)資源。這種模式可以提高應(yīng)用程序的穩(wěn)定性。

背景和問題

該通信的應(yīng)用程序與在云中運(yùn)行的元素必須是可能發(fā)生在這樣的環(huán)境中的瞬時(shí)故障敏感。這些故障包括網(wǎng)絡(luò)連接的過程中出現(xiàn)時(shí),一個(gè)服務(wù)是忙碌的瞬時(shí)損失的組件和服務(wù)中,服務(wù)的臨時(shí)不可用,或超時(shí)。

這些故障一般是自校正的,如果經(jīng)過一個(gè)合適的延遲被重復(fù)觸發(fā)一個(gè)故障的動(dòng)作很可能是成功的。例如,數(shù)據(jù)庫服務(wù),它正在處理大量并發(fā)請(qǐng)求可以實(shí)現(xiàn)節(jié)流策略,暫時(shí)拒絕,直到它的工作量有所緩和任何進(jìn)一步的請(qǐng)求。試圖訪問該數(shù)據(jù)庫的應(yīng)用程序可能無法連接,但如果它經(jīng)過一個(gè)合適的延遲再次嘗試它可能會(huì)成功。

解決方案

在云中,瞬時(shí)故障的情況并不少見和應(yīng)用應(yīng)該被設(shè)計(jì)為優(yōu)雅和透明地處理它們,減少的影響,這種故障可能對(duì)應(yīng)用程序正在執(zhí)行業(yè)務(wù)任務(wù)。

如果一個(gè)應(yīng)用程序檢測到故障時(shí),它試圖將請(qǐng)求發(fā)送到遠(yuǎn)程服務(wù),它可以通過使用以下策略處理失?。?/p>

  • 如果故障指示故障不是瞬時(shí)的或不太成功,如果重復(fù)(例如,所造成的無效提供憑據(jù)的認(rèn)證失敗是不可能成功的,無論是多少次未遂),應(yīng)用程序應(yīng)中止操作和報(bào)告一個(gè)合適的異常。
  • 如果報(bào)道的具體故障是不尋常的或罕見的,這可能是由于反常的情況,如網(wǎng)絡(luò)數(shù)據(jù)包成為損壞,同時(shí)它被發(fā)送。在這種情況下,應(yīng)用程序可以再次立即重試失敗的請(qǐng)求,因?yàn)橄嗤墓收鲜遣豢赡鼙恢貜?fù)和請(qǐng)求將可能是成功的。
  • 如果故障是由一種更加普遍的連接,或“忙”的失敗,網(wǎng)絡(luò)或服務(wù)可能需要在短期內(nèi)同時(shí)連接問題糾正或工作的積壓被清除。應(yīng)用程序應(yīng)該等待請(qǐng)求重試前一個(gè)合適的時(shí)間。

對(duì)于比較常見的短暫故障,重試期間,應(yīng)選擇以傳播從應(yīng)用程序中盡可能均勻的多個(gè)實(shí)例的請(qǐng)求。這可以減少繁忙的業(yè)務(wù)持續(xù)過載的可能性。如果一個(gè)應(yīng)用程序的多個(gè)實(shí)例不斷轟擊與重試請(qǐng)求的服務(wù),則可能需要該服務(wù)更長的時(shí)間來恢復(fù)。

如果請(qǐng)求仍然失敗,應(yīng)用程序可以等待進(jìn)一步的時(shí)期,再次嘗試。如果需要的話,這個(gè)過程可以重復(fù)而增加重試的延遲,直到請(qǐng)求的某一最大數(shù)目已經(jīng)嘗試都失敗了。延遲時(shí)間可以逐步增加,或可使用的定時(shí)策略,如指數(shù)回退,取決于故障的性質(zhì)和可能性,這將在這段時(shí)間內(nèi)被校正。

圖1示出了這種模式。如果嘗試后的預(yù)定數(shù)量的請(qǐng)求不成功,應(yīng)用程序應(yīng)將故障為異常,并相應(yīng)地處理它。

圖1 - 使用重試模式中調(diào)用托管服務(wù)的操作

應(yīng)用程序應(yīng)該換所有試圖訪問遠(yuǎn)程服務(wù),實(shí)現(xiàn)重試政策配套上面列出的策略之一的代碼。發(fā)送到不同的服務(wù)請(qǐng)求會(huì)受到不同的政策,有的供應(yīng)商提供封裝這種方法庫。這些庫通常執(zhí)行的政策是參數(shù)化的,而應(yīng)用程序開發(fā)人員可以指定,如重試次數(shù)和重試之間的時(shí)間項(xiàng)的值。

在檢測故障和重試失敗的操作都應(yīng)該記錄這些故障的詳細(xì)信息的應(yīng)用程序的代碼。這個(gè)信息可能是有用的運(yùn)算符。如果一個(gè)服務(wù)被頻繁報(bào)道為不可用或忙,往往是因?yàn)樵摲?wù)已耗盡其資源。則可以減少與這些故障發(fā)生時(shí)通過換算出該服務(wù)的頻率。例如,如果數(shù)據(jù)庫服務(wù)正在不斷超載,它可能是有利的分區(qū)數(shù)據(jù)庫和負(fù)載分散到多個(gè)服務(wù)器。

注意: 微軟 Azure 提供了重試模式的廣泛支持。該模式與實(shí)踐瞬態(tài)故障處理塊允許應(yīng)用程序通過一系列的重試策略來處理許多 Azure 服務(wù)瞬態(tài)故障。微軟實(shí)體框架版本6提供了用于重新嘗試數(shù)據(jù)庫操作。此外,許多在 Azure Service Bus 和 Azure 存儲(chǔ)的 API 透明地執(zhí)行重試邏輯。

問題和注意事項(xiàng)

在決定如何實(shí)現(xiàn)這個(gè)模式時(shí),您應(yīng)考慮以下幾點(diǎn):

  • 重試政策應(yīng)進(jìn)行調(diào)整,以滿足應(yīng)用和故障性質(zhì)的業(yè)務(wù)需求。它可能是更好一些非關(guān)鍵操作失敗快而不是重試幾次,并影響應(yīng)用程序的吞吐量。例如,在試圖訪問遠(yuǎn)程服務(wù)的交互式Web應(yīng)用程序,這可能是更好的重試之后用重試之間只有一個(gè)短的延遲的數(shù)量較少失敗,并顯示一個(gè)適當(dāng)?shù)南⒔o用戶(例如,“請(qǐng)稍后“),再次嘗試阻止應(yīng)用程序變得反應(yīng)遲鈍。對(duì)于批處理應(yīng)用程序,它可以是更合適的,以增加重試嘗試的次數(shù)與嘗試之間的指數(shù)增加的延遲。
  • 與嘗試之間最小的延遲和大量的重試的高攻擊重試的政策,可能會(huì)進(jìn)一步降低正在接近運(yùn)行或容量的占用。此重試策略也可能會(huì)影響應(yīng)用程序的響應(yīng),如果它被不斷地在嘗試執(zhí)行失敗的操作,而不是做有用功。
  • 如果后一個(gè)顯著次數(shù)的重試請(qǐng)求仍然失敗,則可能是更好的應(yīng)用程序,以防止進(jìn)一步的請(qǐng)求將要在相同的資源為一個(gè)周期,并簡單地立即報(bào)告故障。當(dāng)期限屆滿后,該應(yīng)用程序可以暫時(shí)允許通過一個(gè)或多個(gè)請(qǐng)求,看看他們是否成功。對(duì)于這一策略的詳細(xì)信息,請(qǐng)參閱斷路器格局。
  • 在由它實(shí)現(xiàn)了一個(gè)重試策略可能需要為冪等的應(yīng)用程序調(diào)用的服務(wù)的操作。例如,發(fā)送到服務(wù)的請(qǐng)求可以被接收和處理成功,但是,由于瞬時(shí)故障,它可能無法發(fā)送響應(yīng),指示該處理已完成。然后在應(yīng)用程序的重試邏輯可能試圖重復(fù)上沒有接收到所述第一請(qǐng)求的假定該請(qǐng)求。
  • 一個(gè)請(qǐng)求到服務(wù)失敗可能由于各種原因而提出不同的異常,根據(jù)故障的性質(zhì)。一些例外可指示故障,可以非常迅速地得到解決,而另一些可能表明該故障持續(xù)時(shí)間更長??赡苁怯幸娴闹卦嚥呗?,調(diào)整基于所述異常的類型的重試嘗試之間的時(shí)間。
  • 考慮如何重試的操作是事務(wù)的一部分,會(huì)影響整體交易的一致性。這可能是有用的微調(diào)對(duì)于事務(wù)性操作的重試政策,最大限度地取得成功的機(jī)會(huì),并減少需要撤消所有交易步驟。
  • 確保所有重試代碼是完全針對(duì)各種故障條件下進(jìn)行測試。檢查它不會(huì)嚴(yán)重影響應(yīng)用程序的性能或可靠性,導(dǎo)致在服務(wù)和資源的過度負(fù)荷,或產(chǎn)生競態(tài)條件或瓶頸。
  • 實(shí)現(xiàn)只在一個(gè)失敗的操作的全方面了解重試邏輯。例如,如果包含的重試策略任務(wù)調(diào)用另一個(gè)任務(wù)還包含一個(gè)重試策略,這個(gè)額外的重試的層可加長的延遲的處理。它可能是更好的配置的低級(jí)任務(wù)失敗快速并報(bào)告失敗返回調(diào)用它的任務(wù)的原因。然后這個(gè)更高級(jí)別的任務(wù)可以決定如何處理是根據(jù)它自己的策略失效。
  • 記錄所有的連接故障,提示了重試,使?jié)撛诘膯栴}與該應(yīng)用程序,服務(wù)或資源可以被識(shí)別是很重要的。
  • 研究是最有可能發(fā)生于一個(gè)服務(wù)或資源發(fā)現(xiàn),如果它們有可能是持久或終端的故障。如果是這樣的話,它可能是更好地處理該故障為異常。該應(yīng)用程序可以報(bào)告或記錄該異常,然后試圖通過調(diào)用另一個(gè)服務(wù),持續(xù)或者(如果有一個(gè)可用的),或通過提供降級(jí)功能。關(guān)于如何檢測和處理持久故障的更多信息,請(qǐng)參閱斷路器格局。

何時(shí)使用這個(gè)模式

使用這種模式:

  • 當(dāng)一個(gè)應(yīng)用程序可能會(huì)經(jīng)歷短暫的故障,因?yàn)樗c遠(yuǎn)程服務(wù)進(jìn)行交互,或訪問遠(yuǎn)程資源。這些故障預(yù)計(jì)將是短暫的,并重復(fù)了以前沒有能夠成功的后續(xù)嘗試的請(qǐng)求。

這種模式可能不適合:

  • 當(dāng)故障很可能是持久的,因?yàn)檫@可能會(huì)影響應(yīng)用程序的響應(yīng)性。該應(yīng)用程序可以簡單地是浪費(fèi)時(shí)間和資源試圖重復(fù)請(qǐng)求是最有可能失敗。
  • 對(duì)于處理故障是不因瞬時(shí)故障,如在應(yīng)用程序的業(yè)務(wù)邏輯引起錯(cuò)誤的內(nèi)部的異常。
  • 作為一種替代解決系統(tǒng)中的可擴(kuò)展性問題。如果一個(gè)應(yīng)用程序有頻繁的“忙”的故障,這是通常指示被訪問的服務(wù)或資源應(yīng)相應(yīng)加大。

例子

本實(shí)施例說明的重試模式的實(shí)現(xiàn)。該 OperationWithBasicRetryAsync 方法,如下所示,通過 TransientOperationAsync 方法異步調(diào)用外部服務(wù)(該方法的細(xì)節(jié)將特定于服務(wù),并從樣本代碼被省略)。

private int retryCount = 3;  
...  
?
public async Task OperationWithBasicRetryAsync()  
{  
  int currentRetry = 0;  
?
  for (; ;)  
  {  
    try  
    {  
      // Calling external service.  
      await TransientOperationAsync();  
?
      // Return or break.  
      break;  
    }  
    catch (Exception ex)  
    {  
      Trace.TraceError("Operation Exception");  
?
      currentRetry++;  
?
      // Check if the exception thrown was a transient exception  
      // based on the logic in the error detection strategy.  
      // Determine whether to retry the operation, as well as how   
      // long to wait, based on the retry strategy.  
      if (currentRetry > this.retryCount || !IsTransient(ex))  
      {  
        // If this is not a transient error   
        // or we should not retry re-throw the exception.   
        throw;  
      }  
    }  
?
    // Wait to retry the operation.  
    // Consider calculating an exponential delay here and   
    // using a strategy best suited for the operation and fault.  
    Await.Task.Delay();  
  }  
}  
?
// Async method that wraps a call to a remote service (details not shown).  
private async Task TransientOperationAsync()  
{  
  ...  
}

調(diào)用此方法的聲明被包裹在一個(gè)循環(huán)一個(gè) try/ catch 塊中封裝。如果調(diào)用 TransientOperationAsync 方法成功,沒有拋出異常的 for 循環(huán)退出。如果 TransientOperationAsync 方法失敗,catch 塊檢查為失敗的原因,并且如果它被認(rèn)為是一個(gè)瞬時(shí)錯(cuò)誤代碼等待一個(gè)短暫的延時(shí),然后重試該操作。

在 for 循環(huán)還跟蹤該操作已經(jīng)嘗試的次數(shù),并且如果代碼失敗三次異常被認(rèn)為是更持久。如果該異常是不是暫時(shí)的,或者是長久的,catch 處理拋出的異常。此異常退出 for 循環(huán),應(yīng)捕獲調(diào)用該OperationWithBasicRetryAsync 方法的代碼。

該 IsTransient 方法,如下所示,檢查是否有特定的一組是相關(guān)的,其中所述代碼運(yùn)行的環(huán)境的異常。一過異常的定義可以根據(jù)被訪問的資源,并在其上執(zhí)行的操作環(huán)境的不同而不同。

private bool IsTransient(Exception ex)  
{  
  // Determine if the exception is transient.  
  // In some cases this may be as simple as checking the exception type, in other   
  // cases it may be necessary to inspect other properties of the exception.  
  if (ex is OperationTransientException)  
    return true;  
?
  var webException = ex as WebException;  
  if (webException != null)  
  {  
    // If the web exception contains one of the following status values   
    // it may be transient.  
    return new[] {WebExceptionStatus.ConnectionClosed,   
                  WebExceptionStatus.Timeout,   
                  WebExceptionStatus.RequestCanceled }.  
            Contains(webException.Status);  
  }  
?
  // Additional exception checking logic goes here.  
  return false;  
}
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)