Polly的重试策略在触发时执行
完全相同的操作。因此,默认情况下您无法更改请求。
但是,在实际重试发生之前,您可以在
onRetryAsync
委托中修改它。
为了演示这一点,我将使用
WireMock.NET库来模拟您的下游系统。
因此,首先让我们创建一个Web服务器,该服务器侦听
localhost
上的
40000
端口,路由为
/api
:
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);
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));
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, _, __) => {
dtoIds.RemoveAt(0);
return Task.CompletedTask;
});
await retryInCaseOfPartialSuccess.ExecuteAsync(async (_) => {
var payload = JsonSerializer.Serialize(dtoIds);
Console.WriteLine(payload);
return await client.PostAsync($"http://localhost:{port}{route}", new StringContent(payload));
}, CancellationToken.None);
}
那么,我们做了什么?
- 创建下游模拟程序以模拟400和200后续响应
- 创建重试策略,如果收到400,则可以修改请求的负载
- 将序列化逻辑和http调用放在重试中,这样我们就可以始终序列化最新的对象列表