/// <summary>
 /// Initializes a new instance of the <see cref="WindowsFileSystemWatcher"/> class.
 /// </summary>
 /// <param name="watcherExecute"> Instance of IWatcherExecute</param>
 /// <param name="retryOption"> Instance of RetryOption, default can be null</param>
 public WindowsFileSystemWatcher(IWatcherExecute watcherExecute, RetryOption retryOption = null)
 {
     // Domain or WindowsService needs to inject IWatcherExecute implementation to this WindowsFileSystemWatcher
     // to avoid coupling of WindowsFileSystemWatcher and WatcherExecute in Persistence Layer
     this.watcherExecute = watcherExecute;
     this.retryOption    = retryOption;
 }
Beispiel #2
0
        /// <summary>
        /// Exponentially with jitter on each retries
        /// </summary>
        /// <typeparam name="T">The type of response.</typeparam>
        /// <param name="option">The retry option.</param>
        /// <param name="context">Request context.</param>
        /// <param name="action">Action to execute.</param>
        /// <param name="predicate">Handle result predicate.</param>
        /// <param name="onRetry">Handle custom on retries.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        /// <remarks>
        /// exponential back-off: 2, 4, 8 etc
        /// plus some jitter: up to 1 second
        /// </remarks>
        public static Task <T> SimpleExponentialJitterBackoff <T>(
            RetryOption option,
            Context context,
            Func <Context, Task <T> > action,
            Func <T, bool> predicate,
            Action <DelegateResult <T>, TimeSpan, Context> onRetry)
        {
            if (option is null)
            {
                throw new ArgumentNullException(nameof(option));
            }

            var jitterer = new Random();

            return(Policy
                   .HandleResult(predicate)
                   .WaitAndRetryAsync(
                       5,
                       retryAttempt =>
            {
                return TimeSpan.FromSeconds(Math.Pow(2, retryAttempt - 1))
                + TimeSpan.FromMilliseconds(jitterer.Next(option.JitterStart, option.JitterEnd));
            },
                       onRetry)
                   .ExecuteAsync(async(arg) => await action(arg).ConfigureAwait(false), context));
        }
        public static void AddNRetry(this IServiceCollection services, Action <RetryOption> actionOptions)
        {
            RetryOption retryOption = new RetryOption();

            actionOptions.Invoke(retryOption);
            services.AddSingleton(retryOption);
        }
        /// <summary>
        /// 检查是否满足重试条件
        /// </summary>
        /// <param name="option"></param>
        /// <param name="record"></param>
        /// <param name="retryCount"></param>
        /// <returns></returns>
        private bool CanRetry(RetryOption option, RequestRecord record, int retryCount)
        {
            if (option == null ||
                option.RetryCount <= 0 ||
                retryCount >= option.RetryCount)
            {
                return(false);
            }

            if (option.CanRetry != null)
            {
                return(option.CanRetry(record));
            }

            if ((option.TransientErrorRetry ?? false) && record.Exception != null)
            {
                if (record.Exception is TimeoutException ||
                    record.Exception is OperationCanceledException ||
                    record.Exception is HttpRequestException)
                {
                    return(true);
                }
                return(false);
            }

            var statusCode = (int)record.Response.StatusCode;
            var method     = record.Response.RequestMessage.Method.ToString();

            if (option.RetryStatusCodes?.Contains(statusCode) ?? false &&
                (option.RetryMethods?.Contains(method, StringComparer.CurrentCultureIgnoreCase) ?? false))
            {
                return(true);
            }
            return(false);
        }
Beispiel #5
0
 /// <summary>
 /// Initializes a new instance of the <see cref="MirrorFreezeCopyService"/> class.
 /// </summary>
 public MirrorFreezeCopyService()
 {
     this.watcherConfig        = new WatcherConfig();
     this.watcherConfigService = new WatcherConfigService(this.watcherConfig);
     this.Precheck();
     this.retryOption                  = this.watcherConfigService.GetRetryOptionFromXMLFile(ConfigFilePath);
     this.listWatcher                  = this.watcherConfigService.PopulateWatchersFromXMLFile(ConfigFilePath);
     this.listWatcherExecute           = new List <WatcherExecute>();
     this.listWindowsFileSystemWatcher = new List <WindowsFileSystemWatcher>();
 }
Beispiel #6
0
        /// <summary>
        /// Create a linear retry delay of 1, 2, 3, 4s.
        /// </summary>
        /// <typeparam name="T">The type of response.</typeparam>
        /// <param name="option">The retry option.</param>
        /// <param name="context">Request context.</param>
        /// <param name="action">Action to execute.</param>
        /// <param name="predicate">Handle result predicate.</param>
        /// <param name="onRetry">Handle custom on retries.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public static Task <T> LinearBackoff <T>(
            RetryOption option,
            Context context,
            Func <Context, Task <T> > action,
            Func <T, bool> predicate,
            Action <DelegateResult <T>, TimeSpan, Context> onRetry)
        {
            if (option is null)
            {
                throw new ArgumentNullException(nameof(option));
            }

            var delay = Backoff.LinearBackoff(TimeSpan.FromMilliseconds(option.MinDelayIsMs), retryCount: option.MaxRetry);

            return(HandleRetry(context, action, predicate, onRetry, delay));
        }
Beispiel #7
0
        /// <summary>
        /// First, new Random() is not quite random, when it comes to reducing concurrency.
        /// You can get multiple instances with the same seed, if many retries are issued simultaneously.
        /// https://github.com/App-vNext/Polly/issues/530.
        /// </summary>
        /// <typeparam name="T">The type of response.</typeparam>
        /// <param name="option">The retry option.</param>
        /// <param name="context">Request context.</param>
        /// <param name="action">Action to execute.</param>
        /// <param name="predicate">Handle result predicate.</param>
        /// <param name="onRetry">Handle custom on retries.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public static Task <T> DecorrelatedJitterBackoffV2 <T>(
            RetryOption option,
            Context context,
            Func <Context, Task <T> > action,
            Func <T, bool> predicate,
            Action <DelegateResult <T>, TimeSpan, Context> onRetry)
        {
            if (option is null)
            {
                throw new ArgumentNullException(nameof(option));
            }

            var maxDelay = TimeSpan.FromSeconds(option.MaxDelayIsMs);
            var delay    = Backoff
                           .DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromMilliseconds(option.MinDelayIsMs), retryCount: option.MaxRetry)
                           .Select(s => TimeSpan.FromTicks(Math.Min(s.Ticks, maxDelay.Ticks)));

            return(HandleRetry(context, action, predicate, onRetry, delay));
        }
Beispiel #8
0
        /// <summary>
        /// Create an retry after backoff retry delay which returned by server.
        /// </summary>
        /// <typeparam name="T">The type of response.</typeparam>
        /// <param name="option">The retry option.</param>
        /// <param name="context">Request context.</param>
        /// <param name="action">Action to execute.</param>
        /// <param name="predicate">Handle result predicate.</param>
        /// <param name="sleepDurationProvider">Sleep duration provider.</param>
        /// <param name="onRetryAsync">Handle custom on retries.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public static Task <T> RetryAfterBackOff <T>(
            RetryOption option,
            Context context,
            Func <Context, Task <T> > action,
            Func <T, bool> predicate,
            Func <int, DelegateResult <T>, Context, TimeSpan> sleepDurationProvider,
            Func <DelegateResult <T>, TimeSpan, int, Context, Task> onRetryAsync)
        {
            if (option is null)
            {
                throw new ArgumentNullException(nameof(option));
            }

            return(Policy
                   .HandleResult(predicate)
                   .WaitAndRetryAsync(
                       retryCount: option.MaxRetry,
                       sleepDurationProvider,
                       onRetryAsync)
                   .ExecuteAsync(async(arg) => await action(arg).ConfigureAwait(false), context));
        }
        /// <summary>
        /// 重试逻辑
        /// </summary>
        /// <param name="option"></param>
        /// <param name="request"></param>
        /// <param name="sending"></param>
        /// <param name="records"></param>
        /// <param name="timeoutToken"></param>
        /// <param name="retryCount"></param>
        /// <returns></returns>
        private async Task <RequestRecord> SendWithRetryAsync(RetryOption option, HttpRequest request,
                                                              Func <HttpRequest, Task <HttpResponse> > sending,
                                                              List <RequestRecord> records, CancellationToken timeoutToken, int retryCount = 0)
        {
            if (timeoutToken.IsCancellationRequested)
            {
                throw new ExecutionHttpException(request, new TimeoutException($"请求超时,在{option.TotalTimeout}ms内未获取到结果"));
            }
            records = records ?? new List <RequestRecord>();
            var record = await HandleException(request, async() =>
            {
                var response      = await sending(request);
                var requestRecord = new RequestRecord
                {
                    Request  = request,
                    Response = response,
                    Duration = response.Duration
                };
                return(requestRecord);
            }, requestRecord =>
            {
                records.Add(requestRecord);
                return(requestRecord);
            });

            if (!CanRetry(option, record, retryCount))
            {
                return(record);
            }
            //重试间隔时间
            var waitTime = option.WaitIntervals[retryCount];

            //等待重试间隔时间
            await Waiting(request, waitTime, option.TotalTimeout, timeoutToken);

            request = request.Clone();
            //开始重试
            return(await SendWithRetryAsync(option, request, sending, records, timeoutToken, ++retryCount));
        }