![](/img/trans.png)
[英]Polly HandleTransientHttpError not catching HttpRequestException
[英]Polly retry not always catching HttpRequestException
我的 .NET Core 3.1 应用程序使用 Polly 7.1.0 重试和隔板策略来实现 http 弹性。 重试策略使用HandleTransientHttpError()
来捕获可能的HttpRequestException
。
现在,使用MyClient
触发的 http 请求有时会返回HttpRequestException
。 其中大约一半被波莉抓住并重试。 然而,另一半最终出现在我的try-catch
块中,我必须手动重试它们。 这发生在最大重试次数用尽之前。
我是如何设法创建一个竞争条件来阻止 Polly 捕获所有异常的? 我该如何解决这个问题?
我使用IHttpClientFactory
注册策略如下。
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<MyClient>(c =>
{
c.BaseAddress = new Uri("https://my.base.url.com/");
c.Timeout = TimeSpan.FromHours(5); // Generous timeout to accomodate for retries
})
.AddPolicyHandler(GetHttpResiliencePolicy());
}
private static AsyncPolicyWrap<HttpResponseMessage> GetHttpResiliencePolicy()
{
var delay = Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromSeconds(1), retryCount: 5);
var retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError() // This should catch HttpRequestException
.OrResult(msg => msg.StatusCode == HttpStatusCode.NotFound)
.WaitAndRetryAsync(
sleepDurations: delay,
onRetry: (response, delay, retryCount, context) => LogRetry(response, retryCount, context));
var throttlePolicy = Policy.BulkheadAsync<HttpResponseMessage>(maxParallelization: 50, maxQueuingActions: int.MaxValue);
return Policy.WrapAsync(retryPolicy, throttlePolicy);
}
触发 http 请求的MyClient
如下所示。
public async Task<TOut> PostAsync<TOut>(Uri requestUri, string jsonString)
{
try
{
using (var content = new StringContent(jsonString, Encoding.UTF8, "application/json"))
using (var response = await httpClient.PostAsync(requestUri, content)) // This throws HttpRequestException
{
// Handle response
}
}
catch (HttpRequestException ex)
{
// This should never be hit, but unfortunately is
}
}
这是一些附加信息,尽管我不确定它是否相关。
HttpClient
是DI 临时注册的,因此每个工作单元有 10 个实例飞来飞去。HttpRequestException
每当我们谈论 Polly 策略时,我们可以区分两种不同的例外情况:
HttpRequestException
)。WebException
)。“其中大约一半被波莉抓住并重试。
然而,另一半最终出现在我的 try-catch-block 中”
如果您的某些重试次数用完,就会发生这种情况。 换句话说,有些请求在 6 次尝试(5 次重试和 1 次初始尝试)中无法成功。
这可以使用以下两种工具之一轻松验证:
onRetry
+ context
Fallback
+ context
onRetry
+ context
onRetry
在触发重试策略时但在睡眠持续时间之前被调用。 委托接收retryCount
。 因此,为了能够连接/关联同一请求的单独日志条目,您需要使用某种相关 ID。 拥有一个的最简单方法可以这样编码:
public static class ContextExtensions
{
private const string Key = "CorrelationId";
public static Context SetCorrelation(this Context context, Guid? id = null)
{
context[Key] = id ?? Guid.NewGuid();
return context;
}
public static Guid? GetCorrelation(this Context context)
{
if (!context.TryGetValue(Key, out var id))
return null;
if (id is Guid correlation)
return correlation;
return null;
}
}
这是一个简化的示例:
要执行的方法
private async Task<string> Test()
{
await Task.Delay(1000);
throw new CustomException("");
}
政策
var retryPolicy = Policy<string>
.Handle<CustomException>()
.WaitAndRetryAsync(5, _ => TimeSpan.FromSeconds(1),
(result, delay, retryCount, context) =>
{
var id = context.GetCorrelation();
Console.WriteLine($"{id} - #{retryCount} retry.");
});
用途
var context = new Context().SetCorrelation();
try
{
await retryPolicy.ExecuteAsync(async (ctx) => await Test(), context);
}
catch (CustomException)
{
Console.WriteLine($"{context.GetCorrelation()} - All retry has been failed.");
}
样品 output
3319cf18-5e31-40e0-8faf-1fba0517f80d - #1 retry.
3319cf18-5e31-40e0-8faf-1fba0517f80d - #2 retry.
3319cf18-5e31-40e0-8faf-1fba0517f80d - #3 retry.
3319cf18-5e31-40e0-8faf-1fba0517f80d - #4 retry.
3319cf18-5e31-40e0-8faf-1fba0517f80d - #5 retry.
3319cf18-5e31-40e0-8faf-1fba0517f80d - All retry has been failed.
Fallback
正如人们所说,只要策略无法成功,它将重新抛出已处理的异常。 换句话说,如果策略失败,那么它将问题升级到下一个级别(下一个外部策略)。
这是一个简化的示例:
政策
var fallbackPolicy = Policy<string>
.Handle<CustomException>()
.FallbackAsync(async (result, ctx, ct) =>
{
await Task.FromException<CustomException>(result.Exception);
return result.Result; //it will never be executed << just to compile
},
(result, ctx) =>
{
Console.WriteLine($"{ctx.GetCorrelation()} - All retry has been failed.");
return Task.CompletedTask;
});
用途
var context = new Context().SetCorrelation();
try
{
var strategy = Policy.WrapAsync(fallbackPolicy, retryPolicy);
await strategy.ExecuteAsync(async (ctx) => await Test(), context);
}
catch (CustomException)
{
Console.WriteLine($"{context.GetCorrelation()} - All policies failed.");
}
样品 output
169a270e-acf7-45fd-8036-9bd1c034c5d6 - #1 retry.
169a270e-acf7-45fd-8036-9bd1c034c5d6 - #2 retry.
169a270e-acf7-45fd-8036-9bd1c034c5d6 - #3 retry.
169a270e-acf7-45fd-8036-9bd1c034c5d6 - #4 retry.
169a270e-acf7-45fd-8036-9bd1c034c5d6 - #5 retry.
169a270e-acf7-45fd-8036-9bd1c034c5d6 - All retry has been failed.
169a270e-acf7-45fd-8036-9bd1c034c5d6 - All policies failed.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.