啟用應(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>
對(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í)行重試邏輯。
在決定如何實(shí)現(xiàn)這個(gè)模式時(shí),您應(yīng)考慮以下幾點(diǎn):
使用這種模式:
本實(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;
}
更多建議: