【发布时间】:2022-03-03 21:18:22
【问题描述】:
我的 .NET Core 3.1 应用使用 Polly 7.1.0 重试和隔板策略来实现 http 弹性。重试策略使用HandleTransientHttpError() 捕获可能的HttpRequestException。
现在使用MyClient 触发的http 请求有时会返回HttpRequestException。其中大约一半被波莉抓住并重试。然而,另一半最终出现在我的 try-catch-block 中,我必须手动重试它们。这发生在最大重试次数用尽之前。
我是如何设法创建一个竞争条件来阻止 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-registered transiently,因此每个工作单元有 10 个实例飞来飞去。 - 每个工作单元,客户端会触发约 400 个 http 请求。
- http 请求很长(5 分钟持续时间,30 MB 响应)
【问题讨论】:
-
我在您的 Polly 设置中没有发现任何明显的错误。您能否创建一个最小的复制品,最好使用本地服务?我会说,问题可能不是 Polly,它是我见过的经过最充分测试的库之一。
-
@MatthiasSchuchart 为什么你有
NotFound的触发器?您是否尝试处理最终的一致性? -
@PeterCsala 我不经常使用的网络服务在背压下返回 404。相同的请求通常在重试或 Web 服务空闲时返回 200。由于我无法区分“真实”和基于负载的 404,不幸的是,我还需要重试“真实”404 5 次。
-
@PeterCsala 同意,网络服务应该使用 429,不幸的是,这超出了我的控制。并且还同意我可以使用断路器给它一些呼吸空间(尽管
DecorrelatedJitterBackoffV2中的指数退避应该达到类似的效果)。然而,问题的关键在于确定,什么可能使 Polly 错过它应该捕获的异常。评估这一点也可能使其他人受益。 -
@MatthiasSchuchardt 我已经留下了答案,请检查一下。关于 CB 与退避:它们不一样。在回退的情况下,允许向目标服务器发出所有即将到来的请求。在 CB 的情况下,所有即将到来的请求都被阻塞,直到 CB 处于
HalfOpen状态时请求成功。
标签: c# http asp.net-core polly