/* This Handler must be configured as the PrimaryHttpMessageHandler to be able to * be invoked during the Polly retry attempts and also swapout the * OperationCanceledException when a request timeout occurs * This Handler has a dual purpose for the above reason */ protected async override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { using (var cts = GetCancellationTokenSource(request, cancellationToken)) { try { // If the Polly WaitAndRetryAsync handler has ran we will have the Retry Info Polly.Context context = request.GetPolicyExecutionContext(); RetryInfo retryInfo = context.GetRetryInfo(); if (retryInfo != null) { // Log the fact that this request is due to a retry _logger.LogWarning($"{DateTime.Now.ToString()} : Retrying {request.Method.ToString()} Request to Resource: {context["Resource"]}"); } var response = await base.SendAsync(request, cts?.Token ?? cancellationToken); return(response); } catch (OperationCanceledException ex) when(!cancellationToken.IsCancellationRequested) { // Throw a TimeoutException with useful message and pass along the InnerException var tex = new TimeoutException($"Request timed out, did not receive response within {(request.GetTimeout() ?? DefaultTimeout).TotalSeconds} seconds", ex.InnerException); // Set the source of the exception tex.Source = "TimeoutHandler"; throw tex; } catch (Exception ex) { throw ex; } } }
static async Task PollyTest() { var atr = new CRL.Core.Remoting.PollyAttribute(); //atr.TimeOutTime = TimeSpan.FromMilliseconds(100); atr.CircuitBreakerCount = 2; atr.RetryCount = 1; var pollyCtx = new Polly.Context(); var response = await PollyExtension.InvokeAsync <string>(atr, async() => { //await Task.Delay(200); throw new Exception("has error"); return(await Task.FromResult(new CRL.Core.Remoting.PollyExtension.PollyData <string>() { Data = "ok" })); }, ""); if (pollyCtx.ContainsKey("msg")) { Console.WriteLine(pollyCtx["msg"]); } //var str = PollyExtension.Invoke(atr, () => // { // throw new Exception("has error"); // //System.Threading.Thread.Sleep(200); // return new PollyExtension.PollyData<string>() { Error = "ok" }; // }, "test"); Console.WriteLine(response.ToJson()); }
/* This Handler must be configured as the PrimaryHttpMessageHandler so that it * gets invoked during the Polly retry attempts and is able to detect the original url and method * This handler swaps out OperationCanceledException when a request timeout occurs * This Handler adds info about the request into the Request Properties and Exception Data * This Handler has 3 purposes but can't be split out as the above functions rely on it being the primary handler. */ protected async override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request == null) { throw new ArgumentNullException(nameof(request)); } using (var cts = GetCancellationTokenSource(request, cancellationToken)) { try { // If the Polly WaitAndRetryAsync handler has ran we will have the Retry Info Polly.Context context = request.GetPolicyExecutionContext(); RetryInfo retryInfo = context.GetRetryInfo(); // The RequestUri and Method will always be the original ones as this code will not be hit // if a redirection occurs as it is handles further down the chain. if (retryInfo == null) { AugmentRequest(request); } else { // It's possible to detect a redirect here during a retry as the new request uses the redirected url string requestUrl = request?.RequestUri?.AbsoluteUri?.ToString(); string resourcePath = (string)request.GetResourcePath(); string originalRequestUrl = (string)request.GetOriginalRequestUrl(); bool isRedirected = false; if (!String.IsNullOrEmpty(requestUrl) && requestUrl != originalRequestUrl) { isRedirected = true; } string redirected = isRedirected ? " Redirected" : ""; string originalRequestMethod = (string)request.GetOriginalRequestMethod(); // Log the fact that this request is due to a retry _logger.LogWarning($"{DateTime.Now.ToString()} SendAsync: Retrying a Failed{redirected} {originalRequestMethod} Request to Resource: {resourcePath} {redirected}Url: {requestUrl}"); } var response = await base.SendAsync(request, cts?.Token ?? cancellationToken); return(response); } catch (OperationCanceledException ex) when(!cancellationToken.IsCancellationRequested) { // Also catches TaskCanceledException as it is a subclass of OperationCanceledException // Throw a TimeoutException with useful message and pass along the InnerException var tex = new TimeoutException($"Request timed out, did not receive response within {(request.GetTimeout() ?? DefaultTimeout).TotalSeconds} seconds", ex.InnerException); // Set the source of the exception tex.Source = "TimeoutHandler"; AugmentException(tex, request); throw tex; } catch (Exception ex) { _logger.LogError($"{DateTime.Now.ToString()} SendAsync: Exception occurred in SendAsync() method, Request Url: {request?.RequestUri?.AbsoluteUri?.ToString()} Exception:\n{ex.ToString()}"); AugmentException(ex, request); throw ex; } } }
public static void SetRetryInfo(this Polly.Context context, RetryInfo retryInfo) { if (context == null) { throw new ArgumentNullException(nameof(context)); } context[PropertyKeys.RetryInfoKey] = retryInfo; }
public static Task <HttpResponseMessage> FallbackValueFactory(Polly.Context context, CancellationToken cancellationToken) { var items = Enumerable.Empty <WeatherForecast>(); var json = System.Text.Json.JsonSerializer.Serialize(items); var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK) { Content = new StringContent(json, Encoding.UTF8, "application/json") }; return(Task.FromResult(response)); }
public static RetryInfo GetRetryInfo(this Polly.Context context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } object retryInfo = null; context.TryGetValue(PropertyKeys.RetryInfoKey, out retryInfo); if (retryInfo != null && retryInfo is RetryInfo) { return((RetryInfo)retryInfo); } return(null); }
protected async Task <T> SendRequestAsync <T>(PollyAttribute pollyAttr, Request.ImitateWebRequest request, string url, string httpMethod, string postArgs, string pollyKey, Func <string, T> dataCall) { var pollyCtx = new Polly.Context(); //var response = await request.SendDataAsync(url, httpMethod.ToString(), postArgs); var pollyData = await PollyExtension.InvokeAsync(pollyAttr, async() => { var res = await request.SendDataAsync(url, httpMethod.ToString(), postArgs); return(new PollyExtension.PollyData <string>() { Data = res }); }, pollyKey); if (!string.IsNullOrEmpty(pollyData.Error)) { ThrowError(pollyData.Error, "500"); } //转换为指定的类型 return(dataCall(pollyData.Data)); }
public static void SetPolicyExecutionContext(this System.Net.Http.HttpRequestMessage request, Polly.Context context) { }
protected virtual System.Threading.Tasks.Task <System.Net.Http.HttpResponseMessage> SendCoreAsync(System.Net.Http.HttpRequestMessage request, Polly.Context context, System.Threading.CancellationToken cancellationToken) { throw null; }