Example #1
0
        public virtual async Task <ApiResponse> SendAsync(HttpMethod method, string resource, HttpContent content = null)
        {
            // Create the context here so we have access to it in the catch block
            Polly.Context context = new Polly.Context();
            //Create the Request
            HttpRequestMessage request = new HttpRequestMessage(method, resource);

            if (content != null)
            {
                request.Content = content;
            }
            // Set the PolicyExecutionContext so that it is available after execution of the request
            // https://github.com/App-vNext/Polly/issues/505
            request.SetPolicyExecutionContext(context);
            AugmentContext(context, resource, request);
            // Make the request
            RequestCounter++;
            LastRequestTimeStamp = DateTime.UtcNow;
            try {
                _logger.LogDebug($"{DateTime.Now.ToString()} : Sending request with Method: {method.ToString()} to Resource: {resource}");
                using (var response = await _client.SendAsync(request, CancellationTokenSource.Token)) {
                    TransferRetryInfo(response.RequestMessage, context);
                    return(await _apiResponseBuilder.GetApiResponse(response, resource));
                }
            } catch (Exception exception) {
                // Handles communication errors such as "Connection Refused" etc.
                // Network failures (System.Net.Http.HttpRequestException)
                // Timeouts (System.IO.IOException)
                AugmentException(exception, resource, request);
                TransferRetryInfo(exception, context);
                return(_apiResponseBuilder.GetApiResponse(exception, request, resource));
            }
        }
Example #2
0
        private static TimeSpan GetRetryWaitDuration(
            int retryAttempt,
            Polly.Context context,
            TimeSpan minRetryDelay,
            TimeSpan maxRetryDelay)
        {
            var    random = context["random"] as Random;
            double prev   = (double)context["prev"];

            const double PFactor = 4.0;

            const double RPScalingFactor = 1 / 1.4d;

            double t    = (retryAttempt - 1) + random.NextDouble();
            double next = Math.Pow(2, t) * Math.Tanh(Math.Sqrt(PFactor * t));

            var formulaValue = next - prev;

            context["prev"] = next;

            long ticks =
                (long)Math.Min(
                    formulaValue * RPScalingFactor * minRetryDelay.Ticks,
                    maxRetryDelay.Ticks);

            return(TimeSpan.FromTicks(ticks));
        }
        public void BulkHeadRejectsExecutionWhenQueueFull()
        {
            _processed.Clear();
            _rejected.Clear();
            bool exceptionThrown = false;
            var  bulkHead        = Policy.Bulkhead(1, 5, context =>
            {
                var id = context["id"];
                Console.Out.WriteLine($"Rejected id {id}");
                _rejected.Add((int)id);
            }); // max concurrency of 2, max queue size of 5

            Parallel.ForEach(_testData, id =>
            {
                try
                {
                    var context = new Polly.Context {
                        ["id"] = id
                    };
                    bulkHead.Execute((ctx) => SlowFunction(id), context);
                }
                catch (BulkheadRejectedException)
                {
                    // keep demo running
                    exceptionThrown = true;
                }
            });

            Assert.IsTrue(exceptionThrown);
            Assert.IsTrue(_processed.Count > 0);
            Assert.IsTrue(_rejected.Count > 0);
            Assert.IsTrue(_rejected.Count > _processed.Count); // we will always reject more since method takes 1 second
        }
Example #4
0
        public async Task <IActionResult> Get(Guid id)
        {
            var client = clientFactory.CreateClient("AzureDevOpsCache");

            var requestEndpoint = $"api/azuredevops/projects/{id}";

            var context = new Polly.Context($"GetProjectById-{id}");

            var response = await cachePolicy.ExecuteAsync(
                c =>
            {
                return(client.GetAsync(requestEndpoint));
            }, context);

            return(!response.IsSuccessStatusCode ?
                   StatusCode((int)response.StatusCode, response.ReasonPhrase) :
                   Ok(JsonConvert.DeserializeObject <Project>(await response.Content.ReadAsStringAsync())));

            //var request = new HttpRequestMessage(HttpMethod.Get, requestEndpoint);
            //request.SetPolicyExecutionContext(context);
            //var response = await cachePolicy.ExecuteAsync(
            //	() =>
            //	{
            //		return client.SendAsync(request);
            //	});

            //return !response.IsSuccessStatusCode ?
            //	StatusCode((int)response.StatusCode, response.ReasonPhrase) :
            //	Ok(JsonConvert.DeserializeObject<Project>(await response.Content.ReadAsStringAsync()));
        }
Example #5
0
        public async Task <IActionResult> Get(Guid id)
        {
            var client = clientFactory.CreateClient("AzureDevOpsWithoutRefit");

            var requestEndpoint = $"api/azuredevops/projects/{id}";
            var request         = new HttpRequestMessage(HttpMethod.Get, requestEndpoint);

            var context = new Polly.Context($"{nameof(ProjectsController)}-{nameof(Get)}-{Guid.NewGuid()}")
                          .WithLogger(logger);

            context.Add(PolicyContextKeys.MyTestKey, "ciao!");

            request.SetPolicyExecutionContext(context);

            var response = await client.SendAsync(request);

            return(!response.IsSuccessStatusCode ?
                   StatusCode((int)response.StatusCode, response.ReasonPhrase) :
                   Ok(JsonConvert.DeserializeObject <Project>(await response.Content.ReadAsStringAsync())));

            //var response = await azureDevOpsApi.GetProject(id);
            //return !response.IsSuccessStatusCode ?
            //	StatusCode((int)response.StatusCode, response.Error.Content) :
            //	Ok(response.Content);
        }
Example #6
0
        // Transfers the RetryInfo from the PolicyExecutionContext into the Request Properties
        private void TransferRetryInfo(HttpRequestMessage request, Polly.Context context)
        {
            RetryInfo retryInfo = context.GetRetryInfo();

            if (retryInfo != null)
            {
                request.SetRetryInfo(retryInfo);
            }
        }
Example #7
0
        // Transfers the RetryInfo from the PolicyExecutionContext into the Exception Data
        private void TransferRetryInfo(Exception exception, Polly.Context context)
        {
            RetryInfo retryInfo = context.GetRetryInfo();

            if (retryInfo != null)
            {
                exception.SetRetryInfo(retryInfo);
            }
        }
Example #8
0
        // Send HttpContent, used by all above methods, accept any of the HttpContent sub-classes
        // FormUrlEncodedContent: A container for name/value tuples encoded using application/x-www-form-urlencoded MIME type.
        // MultipartContent: Provides a collection of HttpContent objects that get serialized using the multipart/* content type specification.
        // MultipartFormDataContent: Provides a container for content encoded using multipart/form-data MIME type. (Use this format if you are uploading a file to a server)
        // StreamContent: Provides HTTP content based on a stream.
        // StringContent: Provides HTTP content based on a string.
        // ObjectContent: Contains a value as well as an associated MediaTypeFormatter that will be used to serialize the value when writing this content.
        // ObjectContent comes from the System.Net.Http.Formatting assembly provided by package Microsoft.AspNet.WebApi.Client
        // HttpContent: A base class representing an HTTP entity body and content headers.
        public virtual async Task <ApiResponse> SendAsync(HttpMethod method, string resourcePath, HttpContent content = null)
        {
            // Create the context here so we have access to it in the catch block
            Polly.Context context = new Polly.Context();
            //Create the Request
            HttpRequestMessage request = new HttpRequestMessage(method, resourcePath);

            if (content != null)
            {
                request.Content = content;
            }
            else
            {
                // content is normally provided for post and put methods
                if (method == HttpMethod.Post || method == HttpMethod.Put)
                {
                    _logger.LogDebug($"{DateTime.Now.ToString()} : SendAsync: The HttpContent is null for POST or PUT request!");
                }
            }
            // Set the PolicyExecutionContext so that it is available after execution of the request
            // https://github.com/App-vNext/Polly/issues/505
            request.SetPolicyExecutionContext(context);
            request.SetResourcePath(resourcePath);
            // Make the request
            RequestCount++;
            PendingRequestCount++;
            LastRequestTimeStamp = DateTime.UtcNow;
            var requestTimer = new Stopwatch();

            try {
                if (content == null)
                {
                    _logger.LogDebug($"{DateTime.Now.ToString()} : SendAsync: Sending request with Method: {method?.ToString()} HttpContent is null, RequestUri: {request.RequestUri}");
                }
                else
                {
                    _logger.LogDebug($"{DateTime.Now.ToString()} : SendAsync: Sending request with Method: {method?.ToString()} HttpContent Type: \"{content?.GetType()?.Name?.ToString()}\" RequestUri: {request.RequestUri}");
                }
                requestTimer.Start();
                using (var response = await _client.SendAsync(request, CancellationTokenSource.Token)) {
                    requestTimer.Stop();
                    TransferRetryInfo(response.RequestMessage, context);
                    return(await _apiResponseBuilder.GetApiResponse(response, resourcePath, requestTimer));
                }
            } catch (Exception exception) {
                // Handles communication errors such as "Connection Refused" etc.
                // Network failures (System.Net.Http.HttpRequestException)
                // Timeouts (System.IO.IOException)
                requestTimer.Stop();
                TransferRetryInfo(exception, context);
                return(_apiResponseBuilder.GetApiResponseForException(exception, request, resourcePath, requestTimer));
            } finally {
                PendingRequestCount--;
            }
        }
Example #9
0
        static async void GetRequest()
        {
            string getUrl     = "http://example.com";
            var    httpClient = new HttpClient();

            // create our retry policy using a separate class
            var policy = new PolicyMaker().CreatePolicy();

            // create a Polly Context (Dictionary) to pass into the retry delegate
            // the retry delegate will set the retry count in Polly Context and pass it back to us
            // Polly Context format is {string (id), string, object}
            Polly.Context context = new Polly.Context(Guid.NewGuid().ToString());

            // RetryCounter is a simple object added to our Polly Context for tracking retries
            RetryCounter retryCounter = new RetryCounter();

            retryCounter.Value = 0;
            context.Add("retry", retryCounter);

            // keep track of successful requests
            int totalRequests = 0;

            try
            {
                using (var client = new HttpClient())
                {
                    totalRequests++;
                    // Retry the following call according to the policy - 15 times.
                    await policy.ExecuteAsync(async token =>
                    {
                        // This code is executed within the Policy

                        // Make a request and get a response
                        string msg = await client.GetStringAsync(getUrl);
                        // Display success message
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.WriteLine("GET was successful");
                        Console.ForegroundColor = ConsoleColor.White;

                        // our Polly context is passed into the delegate below
                    }, context);
                }
            }
            catch (Exception e)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Request " + (context["retry"] as RetryCounter).Value + " eventually failed with: " + e.Message);
                Console.ForegroundColor = ConsoleColor.White;
            }
            finally
            {
                Console.WriteLine("\nWe retried " + (context["retry"] as RetryCounter).Value + " times.");
                Console.WriteLine("\nHit ENTER to try again\n\tOR\nType Q and hit ENTER to quit");
            }
        }
        public async Task <HttpResponseMessage> Get()
        {
            var context = new Polly.Context("8888");

            context["TestValue"] = "test";

            var httpClient         = _clientFactory.CreateClient("Test");
            var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://www.gooasdasdasdgle.com");

            httpRequestMessage.SetPolicyExecutionContext(context);

            return(await httpClient.SendAsync(httpRequestMessage));
        }
Example #11
0
        protected override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            _request           = request;
            _cancellationToken = cancellationToken;

            Context context = new Polly.Context {
                { "retrycount", 0 }
            };

            return(Policy
                   .Handle <HttpRequestException>()
                   .WaitAndRetryAsync(5, (n) => TimeSpan.FromSeconds(n), (Action <Exception, TimeSpan, int, Context>)LogFailure)
                   .ExecuteAsync(() => ((Func <Context, Task <HttpResponseMessage> >)ExecuteAsync)(context)));
        }
Example #12
0
        public async Task ExecuteLab()
        {
            var serviceProvider = _services.BuildServiceProvider();
            var clientFactory   = serviceProvider.GetService <IHttpClientFactory>();
            var client          = clientFactory.CreateClient("CustomerService");

            // setup timers/cancellation token
            var outOfTimeToken = new CancellationTokenSource(60000);
            var sw             = new Stopwatch();

            sw.Start();

            // send to api
            while (_customerQueue.Count > 0 && !outOfTimeToken.IsCancellationRequested)
            {
                var customer = _customerQueue.Dequeue();
                var context  = new Polly.Context();
                context["Customer"] = customer;
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "api/lab/customers");
                request.SetPolicyExecutionContext(context);
                request.Content = new StringContent(JsonConvert.SerializeObject(customer), Encoding.UTF8,
                                                    "application/json");

                try
                {
                    var response = await client.SendAsync(request, outOfTimeToken.Token);

                    await Console.Out.WriteLineAsync($"{sw.ElapsedMilliseconds:N}ms - {response.StatusCode}");
                }
                catch (Exception e)
                {
                    break; // abort test on exception
                }
            }

            sw.Stop();

            // verify all customers received
            var verifyClient   = clientFactory.CreateClient("VerificationService");
            var verifyResponse = await verifyClient.GetAsync("api/lab/customers");

            var json = await verifyResponse.Content.ReadAsStringAsync();

            var savedCustomerList = JsonConvert.DeserializeObject <List <Customer> >(json);

            Assert.AreEqual(0, _customerQueue.Count);
            Assert.AreEqual(100, savedCustomerList.Select(x => x.CustomerId).Distinct().Count());
            Assert.IsTrue(sw.ElapsedMilliseconds < 60000);
        }
        private async Task <T> HttpInvoker <T>(string origin, Func <Task <T> > action)
        {
            var normalizedOrigin = NormalizeOrigin(origin);

            if (!_policyWrappers.TryGetValue(normalizedOrigin, out PolicyWrap policyWrap))
            {
                policyWrap = Policy.WrapAsync(_policyCreator(normalizedOrigin).ToArray());
                _policyWrappers.TryAdd(normalizedOrigin, policyWrap);
            }

            // Executes the action applying all
            // the policies defined in the wrapper
            var context = new Polly.Context(normalizedOrigin);

            return(await policyWrap.ExecuteAsync(action));
        }
Example #14
0
        public async Task <IReadOnlyCollection <WeatherForecast> > GetWeather(string endpoint)
        {
            var request = new HttpRequestMessage(HttpMethod.Get, endpoint);

            // pass relevant helpers
            var context = new Polly.Context().WithLogger <WeatherClientTyped>(logger);

            context.WithServiceResolver(this.serviceResolver);
            request.SetPolicyExecutionContext(context);

            var result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);

            result.EnsureSuccessStatusCode();
            using (var contentStream = await result.Content.ReadAsStreamAsync())
            {
                return(await System.Text.Json.JsonSerializer.DeserializeAsync <List <WeatherForecast> >(contentStream, DefaultJsonSerializerOptions.Options));;
            }
        }
Example #15
0
        public async Task HandleSingleException()
        {
            // create policy
            var fallBackPolicy = Policy <string>
                                 .Handle <HttpRequestException>()
                                 .FallbackAsync <string>((ctx, ct) => Task.FromResult((string)ctx["DefaultValue"]), onFallbackAsync: PolicyLoggingHelper.LogFallbackAsync);

            // create context
            // you can put ANY object in your context that you need for logging or performing any other actions in your policies
            // for demo I am setting the default value, but this could be an id, a full customer data model, etc.
            var context = new Polly.Context {
                ["DefaultValue"] = "Default!"
            };

            // execute code wrapped in policy
            var result = await fallBackPolicy.ExecuteAsync((ctx) => DemoHelper.DemoClient.GetStringAsync("api/demo/error"), context);

            Assert.AreEqual("Default!", result);
        }
Example #16
0
        public async Task HandleHttpErrorResponses()
        {
            // create policy
            var fallBackPolicy = Policy
                                 .HandleResult <HttpResponseMessage>(resp => !resp.IsSuccessStatusCode)
                                 .FallbackAsync(FallbackAction, PolicyLoggingHelper.LogFallbackAsync);

            // setup context
            var context = new Polly.Context();

            context["DefaultValue"] = "Default2!";

            // execute code wrapped in policy
            var response = await fallBackPolicy.ExecuteAsync((ctx) => DemoHelper.DemoClient.GetAsync("api/demo/error"), context);

            var content = await response.Content.ReadAsStringAsync();

            Assert.AreEqual("Default2!", content);
        }
Example #17
0
    public async ValueTask <EventHandlingStatus> HandleEvent(IMessageConsumeContext context)
    {
        const string retryKey = "eventuous-retry";

        var pollyContext = new PollyContext {
            { retryKey, new RetryCounter() }
        };

        return(await _retryPolicy.ExecuteAsync(Execute, pollyContext).NoContext());

        async Task <EventHandlingStatus> Execute(PollyContext ctx)
        {
            try {
                return(await _inner.HandleEvent(context).NoContext());
            }
            catch (Exception e) {
                var counter = ctx[retryKey] as RetryCounter;
                Log.FailedToHandleMessageWithRetry(DiagnosticName, context.MessageType, counter !.Counter, e);
                counter.Increase();
                throw;
            }
        }
    }
Example #18
0
 public PollyContextMessageHandler(Polly.Context context = null)
 {
     this.context = context;
 }
Example #19
0
 public static void ReportPollyError(DelegateResult <int> e, TimeSpan tiempo, int intento, Polly.Context contexto)
 {
     Logger.LogInformation($"Polly重试失败:  重试次数: {intento:00} (调用时长: {tiempo.TotalMinutes} min)\t执行时间: {DateTime.Now}");
 }
Example #20
0
 private void AugmentContext(Polly.Context context, string resource, HttpRequestMessage request)
 {
     context["Url"]        = new Uri(_client.BaseAddress, resource).ToString();
     context["Resource"]   = resource;
     context["HttpMethod"] = request.Method.ToString();
 }
Example #21
0
 private void OnBreak(DelegateResult <ISearchResponse <InventoryItem> > delegateResult, TimeSpan timeSpan, Polly.Context context)
 {
     Console.WriteLine($"\t\t\t\t\tConnection break: {delegateResult.Result.IsValid}");
 }
Example #22
0
 private void OnReset(Polly.Context context)
 {
     Console.WriteLine("\t\t\t\t\tConnection reset");
 }
Example #23
0
        public override async Task Invoke(AspectContext context, AspectDelegate next)
        {
            //取出来一个策略
            Policies.TryGetValue(context.ServiceMethod, out Policy policy);
            //防止多线程设置策略出现安全问题,先锁住它,等到策略赋值结束后再解锁
            lock (Policies)
            {
                //如果标注的这个方法上的策略是空的,就开始按照设定新建策略
                if (policy == null)
                {
                    policy = Policy.NoOpAsync();
                    //先进行外层熔断策略
                    if (EnableCircuitBreaker)
                    {
                        // Console.WriteLine("熔断毫秒数" + DurationOfBreak);
                        policy = policy.WrapAsync(Policy.Handle <Exception>().CircuitBreakerAsync(ExceptionsAllowedBeforeBreaking, TimeSpan.FromMilliseconds(DurationOfBreak)));
                    }

                    //超时策略
                    if (TimeOutMilliseconds > 0)
                    {
                        //Console.WriteLine("超时" + TimeOutMilliseconds);
                        policy = policy.WrapAsync(Policy.TimeoutAsync(() => TimeSpan.FromMilliseconds(TimeOutMilliseconds), Polly.Timeout.TimeoutStrategy.Pessimistic));
                    }
                    //重试策略
                    if (RetryTimes > 0)
                    {
                        // Console.WriteLine("重试" + RetryTimes);
                        policy = policy.WrapAsync(Policy.Handle <Exception>().WaitAndRetryAsync(RetryTimes, p => TimeSpan.FromMilliseconds(RetryIntervalMilliseconds)));
                    }

                    //降级策略

                    Policy policyFallBackAsync = Policy.Handle <Exception>().FallbackAsync(async(ctx, c) => {
                        //拿到降级方法
                        MethodInfo methodinfo = context.ServiceMethod.DeclaringType.GetMethod(_fallBackMethod);
                        //执行降级方法,并得到降级方法的返回值
                        Object fallBackResult = methodinfo.Invoke(context.Implementation, context.Parameters);//注意了降级方法的参数必须和标注的方法一致,否则就不知道给降级方法传递什么参数了
                        //让降级方法的返回值作为标注方法的返回值,这次不使用context.ReturnValue,因为如果被标注的方法已经有了策略后,再次执行的还是上次的context,我们不能取上次的context只能取当前这次的context
                        AspectContext aspectContext = (AspectContext)ctx["aspectContext"];
                        aspectContext.ReturnValue   = fallBackResult;
                    }, async(ex, c) => {
                        //异常处理
                        //Console.WriteLine(ex.ToString());
                    }
                                                                                           );

                    //降级策略处于最外层
                    policy = policyFallBackAsync.WrapAsync(policy);
                    //将方法和降级策略放入字典
                    Policies.TryAdd(context.ServiceMethod, policy);
                }
            }

            //临时把aspectcontext 的context存放到polly中,以供每一次策略执行时使用
            Polly.Context plyContext = new Polly.Context();;
            plyContext["aspectContext"] = context;

            //执行策略
            await policy.ExecuteAsync(p => next(context), plyContext);
        }
Example #24
0
 private static void OnHttpRetry(DelegateResult <HttpResponseMessage> result, TimeSpan timeSpan, int retryCount, Polly.Context context, ILogger logger)
 {
     if (result.Result != null)
     {
         logger.LogWarning("Request failed with {StatusCode}. Waiting {timeSpan} before next retry. Retry attempt {retryCount}", result.Result.StatusCode, timeSpan, retryCount);
     }
     else
     {
         logger.LogWarning("Request failed because network failure. Waiting {timeSpan} before next retry. Retry attempt {retryCount}", timeSpan, retryCount);
     }
 }
Example #25
0
            private static Task Execute(Polly.Context c)
            {
                var ep = Params.Get(c);

                return(ep.Next.Execute(ep.Context, ep.Value));
            }