Polly重试-通过所有执行,直到重试成功



当前,Polly Retry策略将独立撤销所有失败的请求。因此,如果有10个请求失败,并且我设置了"永远重试"策略,那么每次重试时,它将再发送10个请求,服务器将永远不会恢复。

如何异步传递所有失败的请求,只重试一个请求,并在重试成功后恢复正常流程?

我不能(不想)使用断路器,因为我的服务是后台工作者服务,断路器破坏了整个后台服务逻辑。

// Current code with only retry policy
var retry = HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryForeverAsync(retryNo => new TimeSpan(0, retryNo > 3 ? 10 : (retryNo * 2), 0));
builder.Services.AddHttpClient<TestClient>().AddPolicyHandler(retry);

用例:我写了一个后台服务,并不断地抓取一个包含30000多个页面的网站。为了防止网站过载,我使用SemaphoreSlim(或Bulkhead)来限制在某个时间点发送到服务器的请求数。

尽管如此,服务器还是有可能拒绝我的请求。此时,我需要重试只有一个失败的请求单元服务器再次开始接受我的请求。由于我同时发送多个请求,Polly正在重试所有失败的请求,这让服务器很不高兴。

预期:

10个请求失败->重试1个请求(单元成功)->如果成功,则重新发送剩余的9个请求。

问题

据我所知,您有一个HttpClient,用于针对同一下游系统发出N个速率受限的并发请求。

您想要处理以下故障场景:

  • 如果出现暂时的网络问题,您希望重试单个请求
  • 如果下游系统过载(所以大多数并发请求都失败了),那么您需要后退,只使用一个请求来检测其健康状况

选项A-合并CB并重试

断路器策略起代理作用。它跟踪传出的通信,如果有太多连续的故障,则阻止进一步的请求。它通过抛出BrokenCircuitException来缩短请求。

在一段时间后,CB将允许针对下游系统发出单个请求,如果成功,则允许所有传出通信,但如果失败,则会缩短它们。在这里我详细介绍了CB是如何工作的。

您可以调整重试策略以了解此异常。这意味着您的重试请求仍将发出,但不会离开您的应用程序域。幸运的是,在Polly中,您可以为一个策略定义多个触发器:

HttpPolicyExtensions
.HandleTransientHttpError()
.Or<BrokenCircuitException>()
.WaitAndRetryForeverAsync(retryNo => new TimeSpan(0, retryNo > 3 ? 10 : (retryNo * 2), 0));

因此,它将触发HttpRequestExceptionBrokenCircuitException。如果HttpStatusCode是408或5xx,它也将触发。

现在剩下的就是将重试和断路器策略结合到一个弹性策略中。您可以使用以下方法之一:

.AddPolicyHandler(retryPolicy.Wrap(cbPolicy))
//OR
.AddPolicyHandler(Policy.Wrap(retryPolicy, cbPolicy))

请注意订购。重要的是将cb注册为内部策略,将retry注册为外部策略,以便能够依赖升级。在这里,我已经详细介绍了这个确切的场景。

注意:如果需要,可以在断路器打开时使用不同的延迟。我在这里详细介绍了如何使用Context对象来实现这一点。


选项B-使用队列

如果应用程序没有崩溃,上述解决方案可以正常工作。如果是这样,那么您必须从头开始整个处理过程。

如果你需要避免这种情况,那么你需要将你的工作项(待处理的url)存储在某个地方。

我建议采用以下架构:

  • 您的主要工作程序不会针对下游系统发出http请求,而是创建作业/工作项
    • 它可以将工作项存储在数据库或持久队列中
  • 还有另一个工作者从数据库或队列中获取作业并尝试执行请求
    • 如果请求成功,则会从永久存储中删除工作项
    • 如果请求失败,那么它不会删除项目,而是获取一个新项目
      • 根据您的要求,您可能需要删除项目并将其推送到队列的末尾<lt;有点重新排队
  • 获取逻辑可以了解断路器状态
    • 如果CB已关闭,则它将获取N个作业
    • 如果它是Open,那么它只获取一个

使用此体系结构,您不需要显式重试策略,因为您的队列/数据库会保留那些未成功的项目。因此,您的获取逻辑将检索相同的作业,直到它最终完成。

您可以通过创建一个死信队列来进一步扩展这一概念,在该队列中可以存储失败N次的工作项。有了它,你的队伍就不会被";"永久";工作项。

最新更新