aliyun-net-sdk-dybaseapi/Dybaseapi/MNS/Runtime/Pipeline/RetryHandler/DefaultRetryPolicy.cs (120 lines of code) (raw):

using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Threading; using Aliyun.Acs.Dybaseapi.MNS.Util; namespace Aliyun.Acs.Dybaseapi.MNS.Runtime.Pipeline.RetryHandler { /// <summary> /// The default implementation of the retry policy. /// </summary> public class DefaultRetryPolicy : RetryPolicy { private int _maxBackoffInMilliseconds = (int)TimeSpan.FromSeconds(30).TotalMilliseconds; // Set of web exception status codes to retry on. private ICollection<WebExceptionStatus> _webExceptionStatusesToRetryOn = new HashSet<WebExceptionStatus> { WebExceptionStatus.ConnectFailure, WebExceptionStatus.ConnectionClosed, WebExceptionStatus.KeepAliveFailure, WebExceptionStatus.NameResolutionFailure, WebExceptionStatus.ReceiveFailure }; // Set of error codes to retry on. private ICollection<string> _errorCodesToRetryOn = new HashSet<string> { "Throttling", "RequestTimeout" }; /// <summary> /// The maximum value of exponential backoff in milliseconds, which will be used to wait /// before retrying a request. /// </summary> public int MaxBackoffInMilliseconds { get { return _maxBackoffInMilliseconds; } set { _maxBackoffInMilliseconds = value; } } /// <summary> /// List of MNS specific error codes which are returned as part of the error response. /// These error codes will be retried. /// </summary> public ICollection<string> ErrorCodesToRetryOn { get { return _errorCodesToRetryOn; } } /// <summary> /// List of WebExceptionStatus for a WebException which will be retried. /// </summary> public ICollection<WebExceptionStatus> WebExceptionStatusesToRetryOn { get { return _webExceptionStatusesToRetryOn; } } /// <summary> /// Constructor for DefaultRetryPolicy. /// </summary> /// <param name="maxRetries">The maximum number of retries before throwing /// back a exception. This does not count the initial request.</param> public DefaultRetryPolicy(int maxRetries) { this.MaxRetries = maxRetries; } /// <summary> /// Returns true if the request is in a state where it can be retried, else false. /// </summary> /// <param name="executionContext">Request context containing the state of the request.</param> /// <returns>Returns true if the request is in a state where it can be retried, else false.</returns> public override bool CanRetry(IExecutionContext executionContext) { return executionContext.RequestContext.Request.IsRequestStreamRewindable(); } /// <summary> /// Return true if the request should be retried. /// </summary> /// <param name="executionContext">Request context containing the state of the request.</param> /// <param name="exception">The exception thrown by the previous request.</param> /// <returns>Return true if the request should be retried.</returns> public override bool RetryForException(IExecutionContext executionContext, Exception exception) { // An IOException was thrown by the underlying http client. if (exception is IOException) { // Don't retry IOExceptions that are caused by a ThreadAbortException if (IsInnerException<ThreadAbortException>(exception)) return false; // Retry all other IOExceptions return true; } // A AliyunServiceException was thrown by ErrorHandler var serviceException = exception as AliyunServiceException; if (serviceException != null) { /* * For 500 internal server errors and 503 service * unavailable errors, we want to retry, but we need to use * an exponential back-off strategy so that we don't overload * a server with a flood of retries. If we've surpassed our * retry limit we handle the error response as a non-retryable * error and go ahead and throw it back to the user as an exception. */ if (serviceException.StatusCode == HttpStatusCode.InternalServerError || serviceException.StatusCode == HttpStatusCode.ServiceUnavailable) { return true; } /* * Throttling is reported as a 400 or 503 error from services. To try and * smooth out an occasional throttling error, we'll pause and retry, * hoping that the pause is long enough for the request to get through * the next time. */ if ((serviceException.StatusCode == HttpStatusCode.BadRequest || serviceException.StatusCode == HttpStatusCode.ServiceUnavailable)) { string errorCode = serviceException.ErrorCode; if (this.ErrorCodesToRetryOn.Contains(errorCode)) { return true; } } WebException webException; if (IsInnerException<WebException>(exception, out webException)) { if (this.WebExceptionStatusesToRetryOn.Contains(webException.Status)) { return true; } } } return false; } /// <summary> /// Checks if the retry limit is reached. /// </summary> /// <param name="executionContext">Request context containing the state of the request.</param> /// <returns>Return false if the request can be retried, based on number of retries.</returns> public override bool RetryLimitReached(IExecutionContext executionContext) { return executionContext.RequestContext.Retries >= this.MaxRetries; } /// <summary> /// Waits before retrying a request. The default policy implements a exponential backoff. /// </summary> /// <param name="executionContext">Request context containing the state of the request.</param> public override void WaitBeforeRetry(IExecutionContext executionContext) { DefaultRetryPolicy.WaitBeforeRetry(executionContext.RequestContext.Retries, this.MaxBackoffInMilliseconds); } public static void WaitBeforeRetry(int retries, int maxBackoffInMilliseconds) { int delay = (int)(Math.Pow(4, retries) * 100); delay = Math.Min(delay, maxBackoffInMilliseconds); AliyunSDKUtils.Sleep(delay); } protected static bool IsInnerException<T>(Exception exception) where T : Exception { T innerException; return IsInnerException<T>(exception, out innerException); } protected static bool IsInnerException<T>(Exception exception, out T inner) where T : Exception { inner = null; var innerExceptionType = typeof(T); var currentException = exception; while (currentException.InnerException != null) { inner = currentException.InnerException as T; if (inner != null) { return true; } currentException = currentException.InnerException; } return false; } } }