ASP.NET Core 2.1 中的 HttpClientFactory (Part 4) 整合Polly实现瞬时故障处理

原文:https://www.stevejgordon.co.uk/httpclientfactory-using-polly-for-transient-fault-handling
发表于:2018年6月

      在本系列的上一篇文章中,我介绍了使用命名和类型客户端注册的DelegatingHandlers的传出中间件的概念。尽管可以使用该方法,但ASP.NET团队希望在大多数情况下,我们无需手动构建自己的处理程序。在某些情况下,库的内置功能可能会提供我们需要的功能。例如,有时将请求包装在时序代码中以跟踪它们执行所需的时间有时会很有用。现在,它已作为默认日志记录的一部分内置到IHttpClientFactory中。在其他情况下,第三方集成可能会提供您所需的功能。例如,基于横切面的思想,在HTTP请求期间处理瞬态故障。在这种情况下,与其制作自己的重试逻辑,不如使用Polly之类的库。
      Polly是一个流行的瞬态故障处理库,它提供了一种机制来定义在发生某些故障时可以应用的策略。重试策略是最常用的策略之一。您可以封装一些代码,如果发生故障,将重试这些代码,有时需要多次重试。这在您的应用程序需要与外部服务进行通信的情况下非常有用。通过HTTP之类的传输工具与服务进行通信时,始终存在瞬态故障的风险。暂时性故障可能会阻止您的请求完成,但也可能是暂时的问题。在这种情况下,使用重试是明智的选择。
      除重试外,Polly还提供了许多其他类型的策略,您可能希望将其中许多策略与重试结合使用,以建立处理故障的复杂方法。我将在本文中介绍一些常见的示例,但是如果您想更全面地介绍,我建议您查看Polly Wiki。
      ASP.NET团队与Polly的主要维护者Dylan和Joel紧密合作,加入了一种集成模式,以使将Polly策略应用于HttpClient实例变得非常简单。
      在使用Polly集成之前,我们需要为项目添加一个包引用。 IHttpClientFactory的常规功能位于Microsoft.Extensions.Http包中,该包作为依赖项包含在Microsoft.AspNetCore.App 2.1元包中。这是ASP.NET Core 2.1中新的元数据包,其中不包含第三方依赖项。因此,为了对IHttpClientFactory使用Polly扩展,我们需要将Microsoft.Extensions.Http.Polly包添加到我们的项目中。
      完成之后,csproj文件将如下所示:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.1.0" />
  </ItemGroup>

</Project>

应用策略

      Microsoft.Extensions.Http.Polly程序包在IHttpClientBuilder上包含一个名为AddPolicyHandler的扩展方法,我们可以使用该方法添加一个处理程序,该处理程序会将使用该客户端实例的所有请求包装在Polly策略中。当我们定义一个命名或类型的客户端时,将返回IHttpClientBuilder。
      我们可以在ConfigureServices方法中使用扩展方法…

services.AddHttpClient("github")
  .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10)));

  在此示例中,我们定义了一个名为“ github”的客户端,并且使用了AddPolicyHandler方法来传递超时策略。您在此处提供的策略必须是IAsyncPolicy <HttpResponseMessage>。此政策将在10秒后使所有请求超时。

重用策略

      在可能的情况下,使用Polly时,最好一次性的定义策略并在应用相同策略的情况下共享它们。这样,要更改策略规则,这些更改仅需要在一个地方进行。此外,它确保仅分配策略一次。当然,如果多个调用者希望通过同一断路器实例运行,则必须共享诸如断路器之类的策略。
      在此示例中,我们将从上一个示例中声明一次超时策略,并与两个命名的客户端共享该策略。

var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10));

services.AddHttpClient("github")
  .AddPolicyHandler(timeoutPolicy);

services.AddHttpClient("google")
  .AddPolicyHandler(timeoutPolicy);

  我们将在本文后面介绍另一种策略重用的选项,使用PolicyRegistry。

 瞬时故障处理

      在处理HTTP请求时,我们要处理的最常见情况是瞬时错误。由于这是一个常见的要求,因此Microsoft.Extensions.Http.Polly软件包包括一个特定的扩展名,我们可以使用该扩展名快速设置处理瞬态故障的策略。
      例如,要在命名客户端的请求发生瞬时故障时添加基本重试,我们可以按以下方式注册重试策略:

services.AddHttpClient("github")
  .AddTransientHttpErrorPolicy(p => p.RetryAsync(3));

      在这种情况下,当满足某些失败条件时,将重试通过客户端发出的所有请求。 AddTransientHttpErrorPolicy方法采用Func <PolicyBuilder <HttpResponseMessage>,IAsyncPolicy <HttpResponseMessage >>。这里的PolicyBuilder将被预先配置为处理HttpRequestExceptions,任何返回5xx状态码的响应以及任何带有408(请求超时)状态码的响应。这应该适合许多情况。如果您要求该策略在其他条件下应用,则需要使用其他重载来传递更具体的策略。
      众所周知,重试时我们需要考虑幂等性。重试HTTP GET是一个非常安全的操作。如果我们拨打了电话但未收到任何回复,则可以安全地重试该电话,而不会造成任何危险。但是,请考虑如果我们重试HTTP POST请求,可能会发生什么。我们必须格外小心,因为有可能实际上收到了您的原始请求,但是我们收到的回复提示失败。在这种情况下,重试可能导致数据重复或损坏下游系统中存储的数据。在这里,您需要更多地了解如果下游服务多次收到相同的请求,下游服务将如何处理。重试操作安全吗?当您拥有下游服务时,更容易控制它。例如,您可能使用一些唯一的标识符来防止重复的POST。
      当您无法控制游系统或知道重复的POST可能会带来负面影响时,您将需要更仔细地控制策略。更合适的选择可能是定义不同的命名/类型客户端。您可以为没有副作用与有副作用的请求分别创建一个客户端。然后,您可以使用正确的客户端来执行操作。但是,这可能会变得有点难以管理。更好的选择是使用AddPolicyHandler的重载,该重载使我们可以访问HttpRequestMessage,以便可以有条件地应用策略。该重载如下所示:
      AddPolicyHandler(Func<HttpRequestMessage, IAsyncPolicy<HttpResponseMessage>> policySelector)
      您会注意到,这里的policySelector委托可以访问HttpRequestMessage,并且应该返回IAsyncPolicy <HttpResponseMessage>。我们无法像前面的示例那样访问PolicyBuilder设置来处理瞬态故障。如果要处理常见的暂时性错误,则需要为我们的政策定义预期条件。为了使此操作更容易,Polly项目包含一个帮助程序扩展,我们可以使用它来设置一个PolicyBuilder,以处理常见的瞬时错误。要使用扩展方法,我们需要从Nuget添加Polly.Extensions.Http包。
      然后,我们可以调用HttpPolicyExtensions.HandleTranisentHttpError()来获取配置有瞬态故障条件的PolicyBuilder。我们可以使用PolicyBuilder创建合适的重试策略,然后在请求为HTTP GET时有条件地应用该策略。在此示例中,任何其他HTTP方法都使用NoOp策略。

var retryPolicy = HttpPolicyExtensions
  .HandleTransientHttpError()
  .RetryAsync(3);

var noOp = Policy.NoOpAsync().AsAsyncPolicy<HttpResponseMessage>();

services.AddHttpClient("github")
  .AddPolicyHandler(request => request.Method == HttpMethod.Get ? retryPolicy : noOp);

使用PolicyRegistry

      最后一个示例是如何通过策略注册表(policy registry)应用策略的基本演示。为了支持策略重用,Polly提供了PolicyRegistry的概念,该概念实质上是策略的容器。可以在应用程序启动时通过将策略添加到注册表中来定义。然后可以将注册表传递出去,并通过名称访问策略。

      IHttpClientBuilder的扩展方法支持使用注册表(registry)将基于Polly的处理程序添加到客户端。

var registry = services.AddPolicyRegistry();

var timeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(30));

registry.Add("regular", timeout);
registry.Add("long", longTimeout);

services.AddHttpClient("github")
	.AddPolicyHandlerFromRegistry("regular");

  首先,我们必须通过DI注册一个PolicyRegistry。 Microsoft.Extensions.Http.Polly软件包包括一些扩展方法,可简化此过程。在上面的示例中,我调用了AddPolicyRegistry方法,该方法是IServiceCollection的扩展。这将创建一个新的PolicyRegistry并通过DI注册,作为IPolicyRegistry <string>和IReadOnlyPolicyRegistry <string>的实现。该方法返回策略,以便我们可以向其添加策略。

      在此示例中,我们添加了两个超时策略并为其指定了名称。现在,在注册客户端时,我们可以调用IHttpClientBuilder上可用的AddPolicyHandlerFromRegistry方法,使用的是策略名称。当工厂创建命名客户端实例时,它会添加适当的处理程序,并调用“regular”重试策略。

总结

      作为Polly的老用户,我很高兴看到IHttpClientFactory添加了这些集成。这些库整合在一起,能够无缝处理瞬态故障,HttpClient实例非常容易启动和运行。我展示的示例非常基础和笼统,但我希望它们能为如何使用和注册策略提供一个思路。有关Polly文档和示例的更多详细信息,建议您查看Polly Wiki。在设计这种集成时,与ASP.NET和Polly团队进行的一些早期讨论非常令人高兴,因为我能够提出策略注册表扩展的有用性。

posted @ 2019-12-30 12:05  lookerblue  阅读(441)  评论(0编辑  收藏  举报