public void TryAsyncActionRecursively<TAsyncResult>(
string asyncActionName,
Func<Task<TAsyncResult>> asyncAction,
Action<int> mainAction,
Action<TAsyncResult> successAction,
Func<string> getContextInfoFunc,
Action<Exception> failedAction,
int retryTimes) where TAsyncResult : AsyncOperationResult
{
var retryAction = new Action<int>(currentRetryTimes =>
{
if (currentRetryTimes > _immediatelyRetryTimes)
{
Task.Factory.StartDelayedTask(_retryIntervalForIOException, () => mainAction(currentRetryTimes + 1));
}
else
{
mainAction(currentRetryTimes + 1);
}
});
var executeFailedAction = new Action<Exception>(ex =>
{
try
{
if (failedAction != null)
{
failedAction(ex);
}
}
catch (Exception unknownEx)
{
_logger.Error(string.Format("Failed to execute the failedCallbackAction of asyncAction:{0}, contextInfo:{1}",
asyncActionName, getContextInfoFunc()), unknownEx);
}
});
var processTaskException = new Action<Exception, int>((ex, currentRetryTimes) =>
{
if (ex is IOException)
{
_logger.Error(string.Format("Async task '{0}' has io exception, contextInfo:{1}, current retryTimes:{2}, try to run the async task again.",
asyncActionName, getContextInfoFunc(), currentRetryTimes), ex);
retryAction(retryTimes);
}
else
{
_logger.Error(string.Format("Async task '{0}' has unknown exception, contextInfo:{1}, current retryTimes:{2}",
asyncActionName, getContextInfoFunc(), currentRetryTimes), ex);
executeFailedAction(ex);
}
});
var completeAction = new Action<Task<TAsyncResult>>(t =>
{
if (t.Exception != null)
{
processTaskException(t.Exception.InnerException, retryTimes);
return;
}
var result = t.Result;
if (result.Status == AsyncOperationResultStatus.IOException)
{
_logger.ErrorFormat("Async task '{0}' result status is io exception, contextInfo:{1}, current retryTimes:{2}, errorMsg:{3}, try to run the async task again.",
asyncActionName, getContextInfoFunc(), retryTimes, result.ErrorMessage);
retryAction(retryTimes);
return;
}
if (successAction != null)
{
successAction(result);
}
});
try
{
asyncAction().ContinueWith(completeAction);
}
catch (IOException ex)
{
_logger.Error(string.Format("IOException raised when executing async action '{0}', contextInfo:{1}, current retryTimes:{2}, try to run the async task again.",
asyncActionName, getContextInfoFunc(), retryTimes), ex);
retryAction(retryTimes);
}
catch (Exception ex)
{
_logger.Error(string.Format("Unknown exception raised when executing async action '{0}', contextInfo:{1}, current retryTimes:{2}",
asyncActionName, getContextInfoFunc(), retryTimes), ex);
executeFailedAction(ex);
}
}
該函數的功能是:執行一個異步任務(返回Task的方法),如果執行出現IO異常,則重試當前主函數(mainAction);用戶的mainAction中會再次調用TryAsyncActionRecursively方法。
從而實現當遇到IO異常時,能做到不斷重試。另外,重試只立即重試指定的次數,超過指定次數,則不立即重試,而是暫停一定間隔後再次執行。
該函數還提供當acyncAction執行成功或失敗後的回調函數,以及允許傳入當前上下文的一些說明信息,以便記錄有意義的錯誤日志信息。
下面是使用示例:
private void PublishEventAsync(ProcessingCommand processingCommand, EventStream eventStream, int retryTimes)
{
TryAsyncActionRecursively<AsyncOperationResult>("PublishEventAsync",
() => _eventPublisher.PublishAsync(eventStream),
currentRetryTimes => PublishEventAsync(processingCommand, eventStream, currentRetryTimes),
result =>
{
_logger.DebugFormat("Publish events success, {0}", eventStream);
processingCommand.Complete(new CommandResult(CommandStatus.Success, processingCommand.Command.Id, null, null, null));
},
() => string.Format("[eventStream:{0}]", eventStream),
ex => processingCommand.Complete(new CommandResult(CommandStatus.Failed, processingCommand.Command.Id, null, null, "Publish events failed.")),
retryTimes);
}
PublishEventAsync(processingCommand, eventStream, 0);