Sea*_*nOB 5 c# dotnet-httpclient polly retry-logic asp.net-core-2.1
我在 Startup.ConfigureServices 方法中在 HttpClient 上创建了重试策略。另请注意,默认情况下,asp.net core 2.1 为 HttpClient 进行的每个调用记录 4 行 [信息] 行,这些行显示在我的问题末尾的日志中。
\nservices.AddHttpClient("ResilientClient")\n .AddPolicyHandler(\n Policy.WrapAsync(\n PollyRetryPolicies.TransientErrorRetryPolicy(),\n Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(60))));\nRun Code Online (Sandbox Code Playgroud)\n该策略定义如下。请注意,我将重试尝试写入日志,因此我会知道是否调用了重试策略。
\npublic static IAsyncPolicy < HttpResponseMessage > TransientErrorRetryPolicy() {\n return HttpPolicyExtensions\n .HandleTransientHttpError()\n .Or < TimeoutRejectedException > ()\n .WaitAndRetryAsync(sleepDurations: ExponentialBackoffPolicy.DecorrelatedJitter(3, SEED_DELAY, MAX_DELAY),\n onRetry: (message, timespan, attempt, context) => {\n context.GetLogger() ? .LogInformation($ "Retrying request to {message?.Result?.RequestMessage?.RequestUri} in {timespan.TotalSeconds} seconds. Retry attempt {attempt}.");\n });\n}\nRun Code Online (Sandbox Code Playgroud)\nHandleTransientHttpError() 是一个 Polly 扩展,它在注释中指出:
\n\n\n配置要处理的条件是:\n\xe2\x80\xa2 网络故障(如 System.Net.Http.HttpRequestException)
\n
我的httpclient用法是这样的:
\nusing (HttpResponseMessage response = await _httpClient.SendAsync(request)) \n{\n response.EnsureSuccessStatusCode();\n\n try \n {\n string result = await response.Content.ReadAsStringAsync();\n if (result == null || result.Trim().Length == 0) {\n result = "[]";\n }\n return JArray.Parse(result);\n } catch (Exception ex) {\n _logger.LogInformation($ "Failed to read response from {url}. {ex.GetType()}:{ex.Message}");\n throw new ActivityException($ "Failed to read response from {url}.", ex);\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n捕获以下日志:
\n[Information] System.Net.Http.HttpClient.ResilientClient.LogicalHandler: Start processing HTTP request GET https://api.au.... obfuscated\n[Information] System.Net.Http.HttpClient.ResilientClient.CustomClientHandler: Sending HTTP request GET https://api.au..... obfuscated\n[Information] System.Net.Http.HttpClient.ResilientClient.CustomClientHandler: Received HTTP response after 2421.8895ms - 200\n[Information] System.Net.Http.HttpClient.ResilientClient.LogicalHandler: End processing HTTP request after 2422.1636ms - OK\n \nUnknown error responding to request: HttpRequestException:\nSystem.Net.Http.HttpRequestException: Error while copying content to a stream. ---> System.IO.IOException: The server returned an invalid or unrecognized response.\n\nat System.Net.Http.HttpConnection.FillAsync()\nat System.Net.Http.HttpConnection.ChunkedEncodingReadStream.CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)\nat System.Net.Http.HttpConnection.HttpConnectionResponseContent.SerializeToStreamAsync(Stream stream, TransportContext context, CancellationToken cancellationToken)\nat System.Net.Http.HttpContent.LoadIntoBufferAsyncCore(Task serializeToStreamTask, MemoryStream tempBuffer)\n--- End of inner exception stack trace ---\nat System.Net.Http.HttpContent.LoadIntoBufferAsyncCore(Task serializeToStreamTask, MemoryStream tempBuffer)\nat System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)\nat nd_activity_service.Controllers.ActivityController.GetND(String url) in /codebuild/output/src251819872/src/src/nd-activity-service/Controllers/ActivityController.cs:line 561\nRun Code Online (Sandbox Code Playgroud)\nHttp 调用成功,我可以看到它返回 200 - OK。但随后会抛出 HttpRequestException。我假设该策略没有被调用,因为 HttpClient 消息管道已经解析,我们可以看到它返回了 200 - OK。那么它是如何抛出异常的呢?
\n我该如何处理?围绕专门处理 HttpRequestExceptions 的方法包装另一个策略?
\n此错误似乎是暂时的。这是一项预定的作业,并在下次调用时开始工作。
\n您的政策是针对而HttpClient不是针对HttpResponseMessage。
因此,即使您收到例如 428,也response.EnsureSuccessStatusCode()不会触发重试。
HandleTransientHttpError如果您从下游系统收到 408 或 5XX 状态代码,将触发重试。当SendAsync抛出HttpRequestException
因为你的异常 StackTrace 看起来像这样:
System.Net.Http.HttpRequestException:将内容复制到流时出错。
System.IO.IOException:服务器返回无效或无法识别的响应。
HttpContent这就是为什么我有根据的猜测是,当您尝试读取响应正文 ( ) 时,类会抛出此异常ReadAsStringAsync。
这不会触发重试,因为您已在 HttpClient 上定义了策略。
如果您想在这些情况下重试,或者当抛出response.EnsureSuccessStatusCode()HRE 或当执行时response.Content.ReadAsStringAsync(),那么您必须将整个 http 通信和响应处理逻辑包装到重试策略中。
让我向您展示如何做到这一点。
首先使用PolicyRegistry而不是AddPolicyHandler:
//services.AddHttpClient("ResilientClient")
// .AddPolicyHandler(
// Policy.WrapAsync(
// TransientErrorRetryPolicy(),
// Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(60))));
services.AddHttpClient("ResilientClient");
var registry = services.AddPolicyRegistry();
registry.Add("retry", Policy.WrapAsync(
TransientErrorRetryPolicy(),
Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(60))));
Run Code Online (Sandbox Code Playgroud)
然后向DI询问寄存器,例如:
private readonly IHttpClientFactory factory;
private readonly IReadOnlyPolicyRegistry<string> registry;
public TestController(IHttpClientFactory factory, IReadOnlyPolicyRegistry<string> registry)
{
this.factory = factory;
this.registry = registry;
}
Run Code Online (Sandbox Code Playgroud)
最后检索组合策略并执行 http 调用:
var retryPolicy = registry.Get<IAsyncPolicy<HttpResponseMessage>>("retry");
await retryPolicy.ExecuteAsync(async () => await IssueRequest());
Run Code Online (Sandbox Code Playgroud)
private async Task<HttpResponseMessage> IssueRequest()
{
var _httpClient = factory.CreateClient("ResilientClient");
HttpResponseMessage response = await _httpClient.GetAsync("http://httpstat.us/428");
response.EnsureSuccessStatusCode();
return response;
}
Run Code Online (Sandbox Code Playgroud)
我使用httpstat.us来模拟 428 响应。
| 归档时间: |
|
| 查看次数: |
5878 次 |
| 最近记录: |