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)); } }
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 }
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())); }
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); }
// 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); } }
// 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); } }
// 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--; } }
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)); }
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))); }
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)); }
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));; } }
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); }
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); }
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; } } }
public PollyContextMessageHandler(Polly.Context context = null) { this.context = context; }
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}"); }
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(); }
private void OnBreak(DelegateResult <ISearchResponse <InventoryItem> > delegateResult, TimeSpan timeSpan, Polly.Context context) { Console.WriteLine($"\t\t\t\t\tConnection break: {delegateResult.Result.IsValid}"); }
private void OnReset(Polly.Context context) { Console.WriteLine("\t\t\t\t\tConnection reset"); }
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); }
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); } }
private static Task Execute(Polly.Context c) { var ep = Params.Get(c); return(ep.Next.Execute(ep.Context, ep.Value)); }