【发布时间】:2021-06-07 17:31:19
【问题描述】:
我有一个对 webapi 端点进行 http 调用的 Azure 函数。我正在关注此示例GitHub Polly RetryPolicy,因此我的代码具有类似的结构。所以在 Startup.cs 我有:
builder.Services.AddPollyPolicies(config); // extension methods setting up Polly retry policies
builder.Services.AddHttpClient("MySender", client =>
{
client.BaseAddress = config.SenderUrl;
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
});
我的重试策略如下所示:
public static class PollyRegistryExtensions
{
public static IPolicyRegistry<string> AddBasicRetryPolicy(this IPolicyRegistry<string> policyRegistry, IMyConfig config)
{
var retryPolicy = Policy
.Handle<Exception>()
.OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.WaitAndRetryAsync(config.ServiceRetryAttempts, retryCount => TimeSpan.FromMilliseconds(config.ServiceRetryBackOffMilliSeconds), (result, timeSpan, retryCount, context) =>
{
if (!context.TryGetLogger(out var logger)) return;
logger.LogWarning(
$"Service delivery attempt {retryCount} failed, next attempt in {timeSpan.TotalMilliseconds} ms.");
})
.WithPolicyKey(PolicyNames.BasicRetry);
policyRegistry.Add(PolicyNames.BasicRetry, retryPolicy);
return policyRegistry;
}
}
我的客户端发件人服务在其构造函数中接收IReadOnlyPolicyRegistry<string> policyRegistry 和IHttpClientFactory clientFactory。我调用客户端的代码如下:
var jsonContent = new StringContent(JsonSerializer.Serialize(contentObj),
Encoding.UTF8,
"application/json");
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, "SendEndpoint")
{
Content = jsonContent
};
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var retryPolicy = _policyRegistry.Get<IAsyncPolicy<HttpResponseMessage>>(PolicyNames.BasicRetry)
?? Policy.NoOpAsync<HttpResponseMessage>();
var context = new Context($"GetSomeData-{Guid.NewGuid()}", new Dictionary<string, object>
{
{ PolicyContextItems.Logger, _logger }
});
var httpClient = _clientFactory.CreateClient("MySender");
var response = await retryPolicy.ExecuteAsync(ctx =>
httpClient.SendAsync(requestMessage), context);
当我尝试在没有运行端点服务的情况下进行测试时,第一次重试尝试时,重试处理程序被触发并且我的记录器记录了第一次尝试。但是,在第二次重试时,我收到一条错误消息:
请求消息已发送。无法发送相同的请求 多次留言
我知道其他人也遇到过类似的问题(请参阅Retrying HttpClient Unsuccessful Requests,解决方案似乎是做我正在做的事情(即使用HttpClientFactory)。但是,如果我定义,我不会遇到这个问题我的重试策略作为启动中配置的一部分如下:
builder.Services.AddHttpClient("MyService", client =>
{
client.BaseAddress = config.SenderUrl;
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}).AddPolicyHandler(GetRetryPolicy());
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromMilliseconds(1000));
}
然后简单地调用我的服务:
var response = await httpClient.SendAsync(requestMessage);
但是这样做我失去了在重试策略上下文中传递我的记录器的能力(这是我在IReadOnlyPolicyRegistry<string> policyRegistry 中注入的全部原因 - 我不能在启动时这样做)。另一个好处是单元测试 - 我可以简单地使用相同的策略注入同一个集合,而无需复制和粘贴一大堆代码并使单元测试变得多余,因为我不再测试我的服务。在启动中定义策略使这成为不可能。所以我的问题是,有没有办法不使用这种方法得到这个重复的请求错误?
【问题讨论】:
标签: c# .net-core azure-functions polly