Polly 的重试策略执行完全相同的操作每当它触发时。因此,默认情况下您无法更改请求。
但你可以在里面修改onRetryAsync
delegate,在实际重试发生之前触发。
为了演示这一点,我将使用WireMock.NET https://github.com/WireMock-Net/WireMock.Net库来模仿您的下游系统。
因此,首先让我们创建一个网络服务器,它监听40000
端口上的localhost
at the /api
route:
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();
然后设置一个scenario http://wiremock.org/docs/stateful-behaviour/模拟第一个请求失败,返回 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 调用放入重试中,以便我们始终可以序列化最新的对象列表