简而言之:我想在重试请求时执行x-HttpMessageHandlers。
实现:目前我添加了一个HttpClient,该请求由Logging-和PolicyHandler处理:
builder.services.AddHttpClient()
.AddHttpMessageHandler<LoggingHandler>()
.AddPolicyHandler(GetRetryPolicy());
政策:为了使政策非常简单,让我们使用一个示例:
public static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
// HandleTransientHttpError: Handles HttpRequestException,
// Http status codes >= 500 (server errors)
// and status code 408 (request timeout)
return HttpPolicyExtensions
.HandleTransientHttpError()
.RetryAsync(3); // Retry the request up to 3 times
}
来源:https://github.com/App-vNext/Polly.Extensions.Http/blob/master/README.md
如果您想测试重试策略,请将请求发送至:https://httpbin.org/status/503。
上面的链接将返回一个不成功的 HttpResponseMessage,状态代码为 502。坏网关。
TL; DR:您只需更改消息处理程序注册顺序
来自
.AddHttpMessageHandler<LogHandler>()
.AddPolicyHandler(retryPolicy);
到
.AddPolicyHandler(retryPolicy)
.AddHttpMessageHandler<LogHandler>();
更新#1
为了更好地理解为什么
DelegatingHandler
的注册顺序很重要,我将扩展我原来的帖子。
AddPolicyHandler
PolicyHttpMessageHandler
实例注册到处理程序管道中。该类以将策略应用于传出请求的方式实现 DelegatingHandler
抽象类。 这就是为什么策略类型应该是 IAsyncPolicy<HttpResponseMessage>
。
这里重要的是,从
HttpClient
的角度来看,你的LogHandler
和这个PolicyHttpMessageHandler
以同样的方式对待。
AddHttpMessageHandler
,AddPolicyHandler
如果您像您一样将处理程序注册到 HttpClient 的管道中,那么输出将是:
Loghandler
Retry attempt 1
Retry attempt 2
让我们看看幕后发生了什么
我使用 mermaid.js 来绘制此图,并且我使用自动编号来能够关联操作和发出的输出。
2: Loghandler
7: Retry attempt 1
10: Retry attempt 2
AddPolicyHandler
,AddHttpMessageHandler
这次我们交换一下注册顺序。输出将是:
Loghandler
Retry attempt 1
Loghandler
Retry attempt 2
Loghandler
动作顺序:
最后是相关日志:
4: Loghandler
8: Retry attempt 1
10: Loghandler
14: Retry attempt 2
16: Loghandler
欲了解更多详情,请查看这个SO主题。
将您的日志记录集成到策略中。这是一个简单的示例。
您的计划
var retryPolicy = Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(response => !response.IsSuccessStatusCode)
.WaitAndRetryAsync(2, retryCount => TimeSpan.FromSeconds(2),
onRetry: (outcome, timespan, retryCount, context) =>
{
var logHandler = new LogHandler();
logHandler.LogRetryAttempt(outcome, retryCount);
});
您的日志处理程序
public class LogHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Debug.WriteLine("LogHandler");
var response = await base.SendAsync(request, cancellationToken);
return response;
}
// Add this method to log retry attempts
public void LogRetryAttempt(DelegateResult<HttpResponseMessage> outcome, int retryCount)
{
// Log the details of the retry attempt
if (outcome.Exception != null)
{
Debug.WriteLine($"Retry {retryCount} due to an exception: {outcome.Exception.Message}");
}
else if (outcome.Result != null)
{
Debug.WriteLine($"Retry {retryCount} due to a non-success status code: {outcome.Result.StatusCode}");
}
}
}