登记服务:
var host = new HostBuilder().ConfigureServices(services =>
{
services.AddHttpClient<Downloader>(client =>
{
client.Timeout = TimeSpan.FromSeconds(1); // -- T1
})
.AddPolicyHandler(HttpPolicyExtensions
.HandleTransientHttpError()
.Or<HttpRequestException>()
.WaitAndRetryAsync(Backoff.DecorrelatedJitterBackoffV2(
TimeSpan.FromSeconds(5), // -- T2
retryCount: 3)))
.AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(10)) // -- T3
.AddPolicyHandler(HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30))); // -- T4
services.AddTransient<Downloader>();
}).Build();Downloader的实现
class Downloader
{
private HttpClient _client;
public Downloader(IHttpClientFactory factory)
{
_client = factory.CreateClient();
}
public void Download(List<Uri> links)
{
await Parallel.ForEachAsync(
links,
async (link, _cancelationToken) =>
{
await _client.GetStreamAsync(uri, _cancellationToken);
});
}
}在这个伪代码中,我对超时与如何/何时重新提交HTTP请求之间的相关性感到困惑。具体地说:
T1、T2、T3和T4是如何“编排”的?我假设如果端点在T1中没有响应,await _client.GetStreamAsync抛出超时异常,那么在与T2相关的时间间隔内,HTTP请求将提交最大的3时间,或者如果断路器计时器到达T4。那么T3的作用是什么呢?HttpMessageHandler相关,我仍然需要按以下方式包装对GetStreamAsync的调用?!Policy
.Handle<Exception>()
.RetryAsync(3)
.ExecuteAsync(
async () => await _client.GetStreamAsync(uri, _cancellationToken));发布于 2022-09-13 09:32:52
首先让我提供一些建议,然后讨论你的问题。
更喜欢Wrap而不是多个AddPolicyHandler
AddPolicyHandler注册一个PolicyHttpMessageHandler,这是一个DelegatingHandler。在您的例子中,您有3个DelegatingHandler,所以异常传播是由ASP.NET核心完成的,而不是Polly。
如果您希望使用Policy.WrapAsync,那么您可以以Polly方式(升级)链接策略。
var T2 = HttpPolicyExtensions
.HandleTransientHttpError()
.Or<HttpRequestException>()
.WaitAndRetryAsync(Backoff.DecorrelatedJitterBackoffV2(
TimeSpan.FromSeconds(5),
retryCount: 3));
var T3 = Policy.TimeoutAsync<HttpResponseMessage>(10);
var T4 = HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));var resilienceStrategy = Policy.WrapAsync<HttpResponseMessage>(T2, T3, T4);
var host = new HostBuilder().ConfigureServices(services =>
{
services
.AddHttpClient<Downloader>(client =>
client.Timeout = TimeSpan.FromSeconds(1))
.AddPolicyHandler(resilienceStrategy);
services.AddTransient<Downloader>();
}).Build();进一步建议
交换超时和断路器
顺便说一句,交换超时和CircuitBreaker策略可能是有意义的。如果超时将是最内在的,您将调整断路器策略,以了解超时问题(.Or<TimeoutRejectedException>()),那么它也可能中断。
var T3 = Policy.TimeoutAsync<HttpResponseMessage>(10);
var T4 = HttpPolicyExtensions
.HandleTransientHttpError()
.Or<TimeoutRejectedException>()
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
var resilienceStrategy = Policy.WrapAsync<HttpResponseMessage>(T2, T4, T3);在更多情况下重试
在超时或中断电路的情况下执行重试也可能是有意义的。
var T2 = HttpPolicyExtensions
.HandleTransientHttpError()
.Or<HttpRequestException>()
.Or<TimeoutRejectedException>()
.Or<BrokenCircuitException>()
.WaitAndRetryAsync(Backoff.DecorrelatedJitterBackoffV2(
TimeSpan.FromSeconds(5),
retryCount: 3));对问题2的答复
请允许我从你的第二个问题开始。
是否所有配置都与客户端和HttpMessageHandler相关,我仍然需要按以下方式包装对
GetStreamAsync的调用?!
不,你不需要那样做。由于您已经用您的弹性策略来修饰您的HttpClient,这就是为什么您不需要对每个HttpClient的方法调用执行同样的操作。
对问题1的答复
请允许我将此分为多个问题。
T1、T2、T3和T4是如何“编排”的?
HttpClient's Timeout)执行全局超时操作。这意味着如果您需要执行几次重试,则在1秒后它们将被取消。因此,对于所有尝试,这都是一个总体的时间限制。。TimeoutExceptionmedianFirstRetryDelay)以指数退避的方式提供了两次重试之间的睡眠持续时间序列和抖动。换句话说,与其每次尝试之间总是等待相同的时间,不如每一次等待的时间越来越多。HttpClient的Timeout的对比,它是一个全局超时。Policy.WrapAsync的最左边参数),那么它也是全局的。breakDuration)充当一个看门人。如果CB中断(在您的情况下连续5次失败之后),那么它将自己转换为Open状态。BrokenCircuitException终止breakDuration之后,它将转换为HalfOpen状态,并允许一个探测。如果成功,CB将移回关闭状态,否则为Open我假设如果端点在T1中没有响应,等待_client.GetStreamAsync抛出异常,然后在与T2相关的时间间隔内,将提交最多3次HTTP请求,或者如果断路器计时器到达T4。那么T3的作用是什么呢?
关于前一点,我想我已经解决了这个问题:)。因为您已经将全局超时设置为1秒,这就是为什么本地超时(10秒)永远不会触发的原因。
如果要为全局超时设置更高的值,则可能触发每个请求(基于重试尝试)超时。
如果上面的任何一点不清楚,请让我知道,我将链接我之前的一些帖子,其中详细讨论了这一点。
更新#1
请您详细介绍一下T1对T3的情况好吗?
在以下这样的主题中,我试图说明全局超时和本地超时之间的区别:
最后是下面是一个SO主题,介绍如何比HttpClient的超时时间更长。
我是否应该实现捕获CB的开放、HalfOpen和关闭状态以及缓冲和保存请求w.r.t的逻辑。状态,或CB内部缓冲区,并在适当时重新提交请求?
断路器不是那样工作的。电路断路器不维护类似请求队列的东西。它只是一个代理,如果下游系统被视为暂时不可用,它可以缩短请求的执行。CB本身没有执行任何重试逻辑。
限幅器政策也以同样的方式工作。在有足够的吞吐量之前,它不会保存请求。
您可以做的是创建一个CB感知的重试逻辑,并将它们组合起来。
https://stackoverflow.com/questions/73696754
复制相似问题