Share an asynchronous task that supports the secondary method of recursive callback when an IO exception occurs. io Recursion
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); }}
This function is used to execute an asynchronous Task (return the Task method). If an IO exception occurs during execution, retry the current main function (mainAction ); the user's mainAction will call the TryAsyncActionRecursively method again.
In this way, when an I/O exception occurs, you can try again. In addition, only the specified number of retries is allowed. If the number of retries is exceeded, the Retry is not performed immediately. Instead, the Retry is paused for a certain interval and then executed again.
This function also provides callback functions when acyncAction is executed successfully or fails, as well as some description information about the current context that can be passed in to record meaningful error log information.
The following is an example:
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);