ASP.Net Core2.1中的HttpClientFactory系列二:集成Polly处理瞬态故障

  前言:最近,同事在工作中遇到了使用HttpClient,有些请求超时的问题,辅导员让我下去调研一下,HttpClinet的使用方式已经改成了之前博客中提到的方式,问题的原因我已经找到了,就是因为使用了伪异步,导致阻塞主线程。在之前的博客中有园友,建议在使用静态的HttpClinet时务必使用它的Async方法,所以就得从头到尾异步化。这一点在之前的文章中没有提,这里作为补充,也感谢这位园友。关于怎么使用异步编程,在这里我就不聊了,大家可以看看其他的博客,看完公司的代码之后,我想强调的是,在使用异步编程的时候,关于返回值的问题:

为什么async方法返回的通常都是Task或者Task<T>,而不是T本身?这是因为,Task和Task<T>代表着在将来某一个时刻将会返回T类型的结果。因此,在主线程调用HttpPostWhitStrBody时,实际上你拿到的是一个未来才会发生的预期,也就是未来的某一个时间会得到一个string的结果。如果返回的是一个T本身,那么,在主线程调用时就会因为访问这个需要一段时间才能给出结果,从而阻塞了主线程。因此,如果async方法有返回值,应返回Task<T>。如果没有返回值,应该返回Task。大家如果不太明白的话,建议多了解一下C#中的异步编程。好了,前戏太多了,下面就来聊聊如何集成Polly。

一、在异步编程中如何处理异常信息

在聊如何集成Polly前,我们先来看看在异步编程中如何处理异常。当异步操作发生异常的时候,异常会停留在异步方法中,调用方法无法直接看到,因此,我们应该异步方法中处理异常,而不是在调用方法中处理异常。如果我们使用了await修饰了任务,那么,只需要为它包上一层try-catch就可以了。当然了,也可以在调用方法(比如Main方法中)捕捉异常,这就需要异常从异步方法中传播给调用方法。做到这件事是很容易的,只需要两个条件:

(1)调用方法本身也是async的,并且,在内部调用异步方法,并使用await。

(2)异步方法返回Task或者Task<T>

因为C#不允许在Main方法中使用async(在C#7.1中,可以使用async修饰Main方法了),因此,我们不得不再创建一层方法,下面通过代码演示一下。

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    class Program
    {
        static void Main(string[] args)
        {

            Caller();

            Console.ReadKey();
        }

        static async void Caller()
        {
            try
            {
                await UseAsync(0);
            }
            catch (AggregateException e)
            {

                Console.WriteLine(e.Message);
            }
        }

        static async Task<bool> UseAsync(int number)
        {
            Console.WriteLine("异步方法运行:"+ Thread.CurrentThread.ManagedThreadId);
            var ret = await Task.Run(() => IsPrimeLowAsync(number));

            return ret;
        }

        static bool IsPrimeLowAsync(int number)
        {
            if (number <= 0) throw new AggregateException("输入必须大于0");
            if (number == 1) return false;

            for (int i = 0; i < number; i++)
            {
                if (number % i == 0) return false;
            }

            return true;
        }
    }
}

一般在处理异常的时候,我们都是采用 try-catch来做处理的,若我们想重试三次,此时我们只能进行循环三次操作。我们只能简单进行处理,自从有了Polly,什么重试机制,超时都不在话下,下面把话题转向Polly。

在聊下面的话题时,建议大家先认真阅读一下这篇博客,因为博主讲的非常细致:Polly

二、集成Polly,处理HTTP请求过程中的瞬时故障

  Polly是一种流行的瞬态故障处理库,它提供了一种机制来定义可在某些故障发生时应用的策略。 最常用的策略之一就是重试策略。 这中策略允许您包装一些代码,如果发生故障,将重试这些代码; 必要时也可以重试多次。 这在您的应用程序需要与外部服务通信的情况下非常有用。 当通过HTTP与服务进行通信时,会出现瞬态故障,这种风险始终存在。 瞬态故障可能会阻碍您的请求完成,但是瞬态故障也可能是暂时性的问题。因此, 这使得在这些情况下重试成为明智的选择。

  除了重试之外,Polly还提供了许多其他类型的策略,其中许多策略可能需要与重试相结合,以构建处理故障的复杂方法。 我将在本文中介绍一些更一般的例子,但是如果你想要更全面的了解,我建议你查看一下Polly wiki

  •   使用Polly

  ASP.NET团队与Polly的主要维护者Dylan和Joel密切合作,使得将Polly策略应用于HttpClient实例非常简单。在开始之前我们先引用下面的两个包:

  

  这个Microsoft.Extensions.Http.Polly包在IHttpClientBuilder上包含一个名为AddPolicyHandler的扩展方法,我们可以使用它来添加一个handler ,该handler 将使用一个Polly实例,来包装请求。

  我们可以用这个扩展在我们的ConfigureServices 方法中,代码如下:

  

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

  在这个例子中,我们定义了一个名字为“github”的客户端,并且我们使用AddPolicyHandler 方法来添加了一种处理超时的策略,这里提供的超时策略,必须是IAsyncPolicy<HttpResponseMessage>,这个中策略在任何请求超过10s都会触发。

  •   重试策略

  如果可能的话,当我们在使用Polly时,最好的尝试是,定义一次策略并在应用相同策略的情况下共享它们,这样要更改策略,只需在一个位置进行更改。此外,它还确保仅分配策略一次。当然了,如果多个使用者希望通过相同的断路器实例运行,则需要共享诸如断路器之类的策略。不太理解,不要紧,下面看代码,体会一下。

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

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

services.AddHttpClient("google")
  .AddPolicyHandler(retryPolicy);
  •   瞬时错误处理

  

处理HTTP请求时,我们要处理的最常的问题就是瞬态故障。 由于这是一个常见的要求,Microsoft.Extensions.Http.Polly软件包中包含一个特定的扩展,我们可以使用它来快速设置处理瞬时故障的策略。

例如,要在指定客户端的请求发生瞬时故障时添加基本重试,我们可以按如下方式注册重试策略:

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

代码的含义是,所以使用命名的HttpClient,发出的请求,只要遇到错误,就会重试三次。这个AddTransientHttpErrorPolicy 方法需要一个Func<PolicyBuilder<HttpResponseMessage>, IAsyncPolicy<HttpResponseMessage>>.类型的参数。此处的PolicyBuilder将预先配置为处理HttpRequestExceptions,任何返回5xx状态代码的响应以及具有408(请求超时)状态代码的任何响应。 这应该适用于许多情况。 如果您要求在其他条件下应用策略,则需要使用不同的重载来传递更具体的策略。

  我们需要意识到, 在进行重试时,我们需要考虑幂等性。 重试HTTP GET是一种非常安全的操作。因为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

  我想在本文中介绍的最后一个示例是如何从策略注册表中应用策略。 为了支持策略重用,Polly提供了PolicyRegistry的概念,PolicyRegistry本质上是策略的容器。 这些可以在应用程序启动时通过向注册表添加策略来定义。 然后可以传递注册表并用于按名称访问策略。IHttpClientBuilder上可用的扩展还支持使用注册表将基于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”重试策略中包含调用,该策略将从注册表中检索。

示例项目:新建一个.Net Core 2.1的webapi项目:

Startup.cs文件的代码:

 public void ConfigureServices(IServiceCollection services)
        {

            services.AddHttpClient("GitHub", client =>
            {
                client.BaseAddress = new Uri("https://api.github.co");
                client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
                client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
            })
          .AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
          {
            TimeSpan.FromSeconds(1),
            TimeSpan.FromSeconds(5),
            TimeSpan.FromSeconds(10)
          }));

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

注意:

WaitAndRetryAsync参数的意思是:每次重试时等待的睡眠持续时间。

ValuesController的代码:

private readonly IHttpClientFactory _httpClientFactory;

        public ValuesController(IHttpClientFactory   httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }
        // GET api/values
        [HttpGet]
        public async  Task<ActionResult> Get()
        {
            var client = _httpClientFactory.CreateClient("GitHub");
            string result = await client.GetStringAsync("/");
            return Ok(result);
        }

看到没,它在重试。

更多的Polly和HttpClinetFactory的集成使用请参考:

https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory

https://www.hanselman.com/blog/AddingResilienceAndTransientFaultHandlingToYourNETCoreHttpClientWithPolly.aspx

三、总结

  注意:AddTransientHttpErrorPolicy方法会自动帮我们处理以下错误:

  (1)Network failures (System.Net.Http.HttpRequestException)

  (2)HTTP 5XX status codes (server errors)

  (3)HTTP 408 status code (request timeout)

  通过这些库,您可以轻松地启动并运行能够无缝处理瞬态故障的HttpClient实例。 有关更详细的Polly文档和示例,建议您查看Polly wiki。这里只是聊了关于HttpClientFactory中集成Polly的基础用法,关于更详细的使用请参考:https://www.cnblogs.com/CreateMyself/p/7589397.html,好了今天就聊到这里,该系列文章还有最后一篇,对于Polly我也是刚接触,至于项目中是否使用还要经过辅导员的审核,希望对你有帮助,谢谢。

参考文章:

(翻译)https://www.stevejgordon.co.uk/httpclientfactory-using-polly-for-transient-fault-handling

作者:郭峥

出处:http://www.cnblogs.com/runningsmallguo/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

原文地址:https://www.cnblogs.com/runningsmallguo/p/9692001.html

时间: 2024-10-08 17:19:22

ASP.Net Core2.1中的HttpClientFactory系列二:集成Polly处理瞬态故障的相关文章

在Node.js中使用RabbitMQ系列二 任务队列

在上一篇文章在Node.js中使用RabbitMQ系列一 Hello world我有使用一个任务队列,不过当时的场景是将消息发送给一个消费者,本篇文章我将讨论有多个消费者的场景. 其实,任务队列最核心解决的问题是避免立即处理那些耗时的任务,也就是避免请求-响应的这种同步模式.取而代之的是我们通过调度算法,让这些耗时的任务之后再执行,也就是采用异步的模式.我们需要将一条消息封装成一个任务,并且将它添加到任务队列里面.后台会运行多个工作进程(worker process),通过调度算法,将队列里的任

asp.net core1.x/asp.net core2.0中如何加载多个配置文件

写这篇文章,来简单的谈一下,asp.net core中,如何加载多配置文件,如有错误请斧正. 在1.x的时候,我们是自己配置 WebHostBuilder而在2.0的时候,ef core团队,将配置写到了一个CreateDefaultBuilder,这是一个预配置,人家为了方便做的. 原文地址:https://www.cnblogs.com/gdsblog/p/8503115.html

一步一步带你做WebApi迁移ASP.NET Core2.0

随着ASP.NET Core 2.0发布之后,原先运行在Windows IIS中的ASP.NET WebApi站点,就可以跨平台运行在Linux中.我们有必要先说一下ASP.NET Core. ASP.NET Core 是新一代的 ASP.NET,第一次出现时的代号为 ASP.NET vNext,后来命名为ASP.NET 5,随着它的完善与成熟,最终命名为 ASP.NET Core,这表明它已不是 ASP.NET 的升级,而是一个重新设计的Web开发框架.而它一个非常重要的变化就是它不再依赖于I

【Web API系列教程】2.1 — ASP.NET Web API中的路由机制

这篇文章描述了ASP.NET Web API如何将HTTP请求发送(路由)到控制器. 备注:如果你对ASP.NET MVC很熟悉,你会发现Web API路由和MVC路由非常相似.主要区别是Web API使用HTTP方法来选择动作(action),而不是URI路径.你也可以在Web API中使用MVC风格的路由.这篇文章不需要ASP.NET MVC的任何知识. 路由表 在ASP.NET Web API中,控制器是一个用于处理HTTP请求的类.控制器中的公共方法被称为动作方法或简单动作.当Web A

从源码中学习设计模式系列——单例模式序/反序列化以及反射攻击的问题(二)

一.前言 这篇文章是学习单例模式的第二篇,之前的文章一下子就给出来看起来很高大上的实现方法,但是这种模式还是存在漏洞的,具体有什么问题,大家可以停顿一会儿,思考一下.好了,不卖关子了,下面我们来看看每种单例模式存在的问题以及解决办法. 二.每种Singleton 模式的演进 模式一 public class LazySingleton { private static LazySingleton lazySingleton = null; private LazySingleton() { }

ASP.NET MVC学习系列(二)-WebAPI请求

继续接着上文 ASP.NET MVC学习系列(一)-WebAPI初探 来看看对于一般前台页面发起的get和post请求,我们在Web API中要如何来处理. 这里我使用Jquery 来发起异步请求实现数据调用. 继续使用上一文章中的示例,添加一个index.html页面,添加对jquery的引用. 一.无参数Get请求 一般的get请求我们可以使用jquery提供的$.get() 或者$.ajax({type:"get"}) 来实现: 请求的后台Action方法仍为上篇文章中的GetU

【ASP.NET Web API教程】4.3 ASP.NET Web API中的异常处理

参考页面: http://www.yuanjiaocheng.net/webapi/create-crud-api-1-delete.html http://www.yuanjiaocheng.net/webapi/Consume-web-api.html http://www.yuanjiaocheng.net/webapi/mvc-consume-webapi-get.html http://www.yuanjiaocheng.net/webapi/mvc-consume-webapi-po

Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化

前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 本文描述ASP.NET Web API中的JSON和XML格式化器. 在ASP.NET Web API中,媒体类型格式化器(Media-type Formatter)是一种能够做以下工作的对象: 从HTTP消息体读取CLR(公共语言运行时)对象 将CLR对象写入HTTP消息体 Web API提供了用于JSON和XML的媒体类

ASP.NET Core2基于RabbitMQ对Web前端实现推送功能

在我们很多的Web应用中会遇到需要从后端将指定的数据或消息实时推送到前端,通常的做法是前端写个脚本定时到后端获取,或者借助WebSocket技术实现前后端实时通讯.因定时刷新的方法弊端很多(已不再采用),所以基于WebSocket技术实现的通讯方案正越来越受大家喜爱,于是在ASP.NET中就有了鼎鼎大名的Signalr.但Signalr不是咱们这里的主角,这里将给大家介绍另一套基于WebSocket的前后端通讯方案,可以给大家在应用中多一个选择. 准备 在开始动手前,咱们先简单介绍下方案的组成部