Polly用不同的请求体重试请求



我以前从未使用过Polly,我不确定这对Polly来说是不是一个好的场景。

我在POST体中调用具有1000个DTO列表的端点。现在,端点将对每个DTO执行一些验证,如果这些dto中的任何一个验证失败,则返回一个HTTP 400 Bad Request,并且响应还将包含所有验证失败的DTOid。因此,即使一个DTO验证失败,我也会得到一个HTTP 400响应。

现在我想知道我是否可以优雅地处理通过验证的剩余dto。

因此,每当我从API获得HTTP 400时,我想更改请求有效负载以删除导致验证失败的DTOs,并使用剩余的DTOs重试请求。

我怎样才能让Polly做到这一点呢?

我使用类型的HttpClient使用HttpClientFactory在。net 5使我的POST请求。

Polly的重试策略执行完全相同的操作无论何时触发。因此,默认情况下,您不能更改请求。

但是您可以在onRetryAsync委托中修改它,该委托在实际重试发生之前被触发。


为了演示这一点,我将使用WireMock。. NET库来模拟下游系统。

因此,首先让我们创建一个web服务器,它在/api路由上监听localhost上的40000端口:

protected const string route = "/api";
protected const int port = 40_000;
protected static readonly WireMockServer server = WireMockServer.Start(port);
protected static readonly IRequestBuilder endpointSetup = Request.Create().WithPath(route).UsingPost();

然后设置一个场景来模拟第一个请求以400失败,然后第二个请求以200成功。

protected const string scenario = "polly-retry-test";
server.Reset();
server
.Given(endpointSetup)
.InScenario(scenario)
.WillSetStateTo(1)
.WithTitle("Failed Request")
.RespondWith(Response.Create().WithStatusCode(HttpStatusCode.BadRequest));
server
.Given(endpointSetup)
.InScenario(scenario)
.WhenStateIs(1)
.WithTitle("Succeeded Request")
.RespondWith(Response.Create().WithStatusCode(HttpStatusCode.OK));

让我们测试一下

protected static readonly HttpClient client = new HttpClient();
var result = await client.PostAsync($"http://localhost:{port}{route}", new StringContent(""));
Console.WriteLine(result.StatusCode);
result = await client.PostAsync($"http://localhost:{port}{route}", new StringContent(""));
Console.WriteLine(result.StatusCode);

的输出将如下所示:

BadRequest
OK

好了,现在我们有了一个下游模拟器,现在是时候关注重试策略了。为了简单起见,我将序列化一个List<int>集合,并将其作为有效负载发布。

我设置重试每当它收到一个400,然后在它的onRetryAsync委托我检查响应并删除不需要的整数。

AsyncRetryPolicy<HttpResponseMessage> retryInCaseOfPartialSuccess = Policy
.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.BadRequest)
.RetryAsync(1, onRetryAsync: (dr, _, __) => {
//TODO: Process response from: `dr.Result.Content`
//TODO: Replace the removal logic to appropriate one
dtoIds.RemoveAt(0);
return Task.CompletedTask;
});

让我们用修饰的重试策略调用下游API:

await retryInCaseOfPartialSuccess.ExecuteAsync(async (_) => {
var payload = JsonSerializer.Serialize(dtoIds);
Console.WriteLine(payload); //Only for debugging purposes
return await client.PostAsync($"http://localhost:{port}{route}", new StringContent(payload));
}, CancellationToken.None);

让我们把这些放在一起:

protected const string scenario = "polly-retry-test";
protected const string route = "/api";
protected const int port = 40_000;
protected static readonly WireMockServer server = WireMockServer.Start(port);
protected static readonly IRequestBuilder endpointSetup = Request.Create().WithPath(route).UsingPost();
protected static readonly HttpClient client = new HttpClient();
private static async Task Main()
{
server.Reset();
server
.Given(endpointSetup)
.InScenario(scenario)
.WillSetStateTo(1)
.WithTitle("Failed Request")
.RespondWith(Response.Create().WithStatusCode(HttpStatusCode.BadRequest));
server
.Given(endpointSetup)
.InScenario(scenario)
.WhenStateIs(1)
.WithTitle("Succeeded Request")
.RespondWith(Response.Create().WithStatusCode(HttpStatusCode.OK));
//var result = await client.PostAsync($"http://localhost:{port}{route}", new StringContent(""));
//Console.WriteLine(result.StatusCode);
//result = await client.PostAsync($"http://localhost:{port}{route}", new StringContent(""));
//Console.WriteLine(result.StatusCode);
await IssueRequestAgainstDownstream(new List<int> { 1, 2 });
}
private static async Task IssueRequestAgainstDownstream(List<int> dtoIds)
{
AsyncRetryPolicy<HttpResponseMessage> retryInCaseOfPartialSuccess = Policy
.HandleResult<HttpResponseMessage>(response => response.StatusCode == HttpStatusCode.BadRequest)
.RetryAsync(1, onRetryAsync: (dr, _, __) => {
//TODO: Process response from: `dr.Result.Content`
//TODO: Replace the removal logic to appropriate one
dtoIds.RemoveAt(0);
return Task.CompletedTask;
});
await retryInCaseOfPartialSuccess.ExecuteAsync(async (_) => {
var payload = JsonSerializer.Serialize(dtoIds);
Console.WriteLine(payload); //Only for debugging purposes
return await client.PostAsync($"http://localhost:{port}{route}", new StringContent(payload));
}, CancellationToken.None);
}

那么,我们做了什么?

  • 创建下游模拟以模拟400和200个后续响应
  • 创建一个重试策略,如果收到400
  • ,可以修改请求的有效负载。
  • 将序列化逻辑和http调用放在重试中,这样我们就可以始终序列化最近的对象列表

相关内容

  • 没有找到相关文章

最新更新