已被.NET基金会认可的弹性和瞬态故障处理库Polly介绍

前言

本节我们来介绍一款强大的库Polly,Polly是一种.NET弹性和瞬态故障处理库,允许我们以非常顺畅和线程安全的方式来执诸如行重试,断路,超时,故障恢复等策略。 Polly针对对.NET 4.0,.NET 4.5和.NET Standard 1.1以及.NET Core实现,该项目作者现已成为.NET基金会一员,项目一直在不停迭代和更新,项目地址【https://github.com/App-vNext/Polly】,你值得拥有。接下来我们以.NET Framework  4.5来演示它的强大功能。

Introduce Polly

首先我们得下载Polly包,最新版本为5.3.1,如下:

该库实现了七种恢复策略,下面我一一为您来介绍。

重试策略(Retry)

重试策略针对的前置条件是短暂的故障延迟且在短暂的延迟之后能够自我纠正。允许我们做的是能够自动配置重试机制。

断路器(Circuit-breaker)

断路器策略针对的前置条件是当系统繁忙时,快速响应失败总比让用户一直等待更好。保护系统故障免受过载,Polly可以帮其恢复。

超时(Timeout)

超时策略针对的前置条件是超过一定的等待时间,想要得到成功的结果是不可能的,保证调用者不必等待超时。

隔板隔离(Bulkhead Isolation)

隔板隔离针对的前置条件是当进程出现故障时,多个失败一直在主机中对资源(例如线程/ CPU)一直占用。下游系统故障也可能导致上游失败。这两个风险都将造成严重的后果。都说一粒老鼠子屎搅浑一锅粥,而Polly则将受管制的操作限制在固定的资源池中,免其他资源受其影响。

缓存(Cache)

缓存策略针对的前置条件是数据不会很频繁的进行更新,为了避免系统过载,首次加载数据时将响应数据进行缓存,如果缓存中存在则直接从缓存中读取。

回退(Fallback)

操作仍然会失败,也就是说当发生这样的事情时我们打算做什么。也就是说定义失败返回操作。

策略包装(PolicyWrap)

策略包装针对的前置条件是不同的故障需要不同的策略,也就意味着弹性灵活使用组合。

几种策略使用

一旦从事IT就得警惕异常并友好拥抱异常而非不闻不问,这个时候我们利用try{}catch{}来处理。

            try
            {
                var a = 0;
                var b = 1 / a;
            }
            catch (DivideByZeroException ex)
            {

                throw ex;
            }

若我们想重试三次,此时我们只能进行循环三次操作。我们只能简单进行处理,自从有了Polly,什么重试机制,超时都不在话下,下面我们来简短介绍各种策略。Polly默认处理策略需要指定抛出的具体异常或者执行抛出异常返回的结果。处理单个类型异常如下:

Policy
  .Handle<DivideByZeroException>()

上述异常指尝试除以0,下面我们演示下具体使用,我们尝试除以0并用Polly指定该异常并重试三次。

        static int Compute()
        {
            var a = 0;
            return 1 / a;
        }
            try
            {
                var retryTwoTimesPolicy =
                     Policy
                         .Handle<DivideByZeroException>()
                         .Retry(3, (ex, count) =>
                         {
                             Console.WriteLine("执行失败! 重试次数 {0}", count);
                             Console.WriteLine("异常来自 {0}", ex.GetType().Name);
                         });
                retryTwoTimesPolicy.Execute(() =>
                {
                    Compute();
                });
            }
            catch (DivideByZeroException e)
            {
                Console.WriteLine($"Excuted Failed,Message: ({e.Message})");

            }

如果我们想指定处理多个异常类型通过OR即可。

Policy
  .Handle<DivideByZeroException>()
  .Or<ArgumentException>()

当然还有更加强大的功能,比如在微信支付时,微信回调我们的应用程序时,此时若失败,想必微信那边也会做重试机制,例如隔一段时间重试调用一次,重复调用几次后仍失败则不再回调。我们利用Polly则可以演示等待重试机制。

        /// <summary>
        /// 抛出异常
        /// </summary>
        static void ZeroExcepcion()
        {
            throw new DivideByZeroException();
        }
        /// <summary>
        /// 异常信息
        /// </summary>
        /// <param name="e"></param>
        /// <param name="tiempo"></param>
        /// <param name="intento"></param>
        /// <param name="contexto"></param>
        static void ReportaError(Exception e, TimeSpan tiempo, int intento, Context contexto)
        {
            Console.WriteLine($"异常: {intento:00} (调用秒数: {tiempo.Seconds} 秒)\t执行时间: {DateTime.Now}");
        }
            try
            {
                var politicaWaitAndRetry = Policy
                    .Handle<DivideByZeroException>()
                    .WaitAndRetry(new[]
                    {
                        TimeSpan.FromSeconds(1),
                        TimeSpan.FromSeconds(3),
                        TimeSpan.FromSeconds(5),
                        TimeSpan.FromSeconds(7)
                    }, ReportaError);
                politicaWaitAndRetry.Execute(() =>
                {
                    ZeroExcepcion();
                });
            }
            catch (Exception e)
            {
                Console.WriteLine($"Executed Failed,Message:({e.Message})");
            }

我们讲完默认策略和重试策略,再来看看反馈策略,翻译的更通俗一点则是执行失败后返回的结果,此时要为Polly指定返回类型,然后指定异常,最后调用Fallback方法。

        static string ThrowException()
        {
            throw new Exception();
        }
           var fallBackPolicy =
                Policy<string>
                    .Handle<Exception>()
                    .Fallback("执行失败,返回Fallback");

            var fallBack = fallBackPolicy.Execute(() =>
            {
                return ThrowException();
            });
            Console.WriteLine(fallBack);

包裹策略说到底就是混合多种策略,并执行。

          var fallBackPolicy =
                Policy<string>
                    .Handle<Exception>()
                    .Fallback("执行失败,返回Fallback");

            var fallBack = fallBackPolicy.Execute(() =>
            {
                return ThrowException();
            });
            Console.WriteLine(fallBack);

            var politicaWaitAndRetry =
                Policy<string>
                    .Handle<Exception>()
                    .Retry(3, (ex, count) =>
                    {
                        Console.WriteLine("执行失败! 重试次数 {0}", count);
                        Console.WriteLine("异常来自 {0}", ex.GetType().Name);
                    });

            var mixedPolicy = Policy.Wrap(fallBackPolicy, politicaWaitAndRetry);
            var mixedResult = mixedPolicy.Execute(ThrowException);
            Console.WriteLine($"执行结果: {mixedResult}");

至此关于Polly的基本介绍就已结束,该库还是非常强大,更多特性请参考上述github例子,接下来我们来看看两种具体场景。

ASP.NET Web APi使用Polly重试机制

在Polly v4.30中以上可以利用HandleResult指定返回结果,如下:

Policy
  .HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.NotFound)

基于此我们完全可以利用执行Web APi中的响应策略,如下:

 public readonly RetryPolicy<HttpResponseMessage> _httpRequestPolicy;

拿到响应中状态码,若为500则重试三次。

 _httpRequestPolicy = Policy.HandleResult<HttpResponseMessage>(
            r => r.StatusCode == HttpStatusCode.InternalServerError)
            .WaitAndRetryAsync(3,
            retryAttempt => TimeSpan.FromSeconds(retryAttempt));

上述获取请求响应策略在构造函数中获取。

    public class PollyController : ApiController
    {
        public readonly RetryPolicy<HttpResponseMessage> _httpRequestPolicy;
        public PollyController()
        {
            _httpRequestPolicy = Policy.HandleResult<HttpResponseMessage>(
            r => r.StatusCode == HttpStatusCode.InternalServerError)
            .WaitAndRetryAsync(3,
            retryAttempt => TimeSpan.FromSeconds(retryAttempt));
        }
    }

此时调用接口时执行策略的Execute或者ExecuteAsync方法即可。

        public async Task<IHttpActionResult> Get()
        {
            var httpClient = new HttpClient();
            string requestEndpoint = "http://localhost:4096";

            HttpResponseMessage httpResponse = await _httpRequestPolicy.ExecuteAsync(() => httpClient.GetAsync(requestEndpoint));

            IEnumerable<string> numbers = await httpResponse.Content.ReadAsAsync<IEnumerable<string>>();

            return Ok(numbers);
        }

你以为仅限于在Web APi中使用吗?在其他框架中也可以使用,例如EntityFramework 6.x中,在EntityFramework 6+上出现了执行策略,也就是执行重试机制,这个时候我们依然可以借助Polly轮子来实现。

EntityFramework 6.x使用Polly重试机制

在EntityFramework 6.x中有如下执行策略接口,看起来是不是和Polly中的Execute方法是不是很类似。

    //
    // 摘要:
    //     A strategy that is used to execute a command or query against the database, possibly
    //     with logic to retry when a failure occurs.
    public interface IDbExecutionStrategy
    {
        //
        // 摘要:
        //     Indicates whether this System.Data.Entity.Infrastructure.IDbExecutionStrategy
        //     might retry the execution after a failure.
        bool RetriesOnFailure { get; }

        //
        // 摘要:
        //     Executes the specified operation.
        //
        // 参数:
        //   operation:
        //     A delegate representing an executable operation that doesn't return any results.
        void Execute(Action operation);
        //
        // 摘要:
        //     Executes the specified operation and returns the result.
        //
        // 参数:
        //   operation:
        //     A delegate representing an executable operation that returns the result of type
        //     TResult.
        //
        // 类型参数:
        //   TResult:
        //     The return type of operation.
        //
        // 返回结果:
        //     The result from the operation.
        TResult Execute<TResult>(Func<TResult> operation);
        //
        // 摘要:
        //     Executes the specified asynchronous operation.
        //
        // 参数:
        //   operation:
        //     A function that returns a started task.
        //
        //   cancellationToken:
        //     A cancellation token used to cancel the retry operation, but not operations that
        //     are already in flight or that already completed successfully.
        //
        // 返回结果:
        //     A task that will run to completion if the original task completes successfully
        //     (either the first time or after retrying transient failures). If the task fails
        //     with a non-transient error or the retry limit is reached, the returned task will
        //     become faulted and the exception must be observed.
        Task ExecuteAsync(Func<Task> operation, CancellationToken cancellationToken);
        //
        // 摘要:
        //     Executes the specified asynchronous operation and returns the result.
        //
        // 参数:
        //   operation:
        //     A function that returns a started task of type TResult.
        //
        //   cancellationToken:
        //     A cancellation token used to cancel the retry operation, but not operations that
        //     are already in flight or that already completed successfully.
        //
        // 类型参数:
        //   TResult:
        //     The result type of the System.Threading.Tasks.Task`1 returned by operation.
        //
        // 返回结果:
        //     A task that will run to completion if the original task completes successfully
        //     (either the first time or after retrying transient failures). If the task fails
        //     with a non-transient error or the retry limit is reached, the returned task will
        //     become faulted and the exception must be observed.
        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> operation, CancellationToken cancellationToken);
    }

EntityFramework 6.x中的执行策略说到底就是数据库连接问题即弹性连接,若考虑到数据库过渡负载问题,此时应用程序和数据库之间存在网络问题的话。可能数据库连接在几秒内才返回,此时也没有什么很大的问题,我们完全可以再尝试一次,此时或许过了连接频繁期,保证连接立马恢复。如果数据库连接一会恢复不了呢?或许是五分钟,又或者是半个小时。如果我们只是一味盲目的进行重试,这显然不可取。如果我们的应用程序连接超时时间超过了20秒,若我们选择继续连接到数据库,我们将很快用完我们应用程序池中的工作线程。一直等待数据库的响应。此时网站将完全无响应,同时会给用户页面无响应的友好提醒。这是Polly库中描述断路器的很好例子,换句话说如果我们捕获了m个数量的SqlExceptions,假设数据库有其他问题存在,导致我们不能在n秒内再尝试连接数据库。此时在数据库连接上存在一个问题,那就是阻塞了我们的应用程序工作线程被挂起,我们试图连接数据库,我们假设不可用的话,但是我们要打破这种不可用,那就用Polly吧。

 

我们看到上述EntityFramework 6.x实现了IDbExecutionStrategy接口,但没有实现如Polly中的断路器模式,EntityFramework 6.x中的执行策略只是重试机制而已。 比如SqlAzureExecutionStrategy将在指定的时间段内重试指定的次数,直到一段时间段过去,重试指数过后,接着就是失败。 同时所有后续调用将执行相同操作,重试并失败。 这是调用数据库时最好的策略吗? 不敢肯定,或许Polly中的断路器模式值得我们借鉴。我们自己来实现上述执行策略接口。

    public class CirtuitBreakerExecutionStrategy : IDbExecutionStrategy
    {
        private Policy _policy;

        public CirtuitBreakerExecutionStrategy(Policy policy)
        {
            _policy = policy;
        }

        public void Execute(Action operation)
        {

            _policy.Execute(() =>
            {
                operation.Invoke();
            });
        }

        public TResult Execute<TResult>(Func<TResult> operation)
        {
            return _policy.Execute(() =>
            {
                return operation.Invoke();
            });
        }

        public async Task ExecuteAsync(Func<Task> operation, CancellationToken cancellationToken)
        {
            await _policy.ExecuteAsync(() =>
            {
                return operation.Invoke();
            });
        }

        public async Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> operation, CancellationToken cancellationToken)
        {
            return await _policy.ExecuteAsync(() =>
            {
                return operation.Invoke();
            });
        }

        public bool RetriesOnFailure { get { return true; } }
    }

接下来在基于代码配置文件中设置我们上述自定义实现的断路器模式。

    public class EFConfiguration : DbConfiguration
    {
        public Policy _policy;
        public EFConfiguration()
        {
            _policy = Policy.Handle<Exception>().CircuitBreaker(3, TimeSpan.FromSeconds(60));

            SetExecutionStrategy("System.Data.SqlClient", () => new CirtuitBreakerExecutionStrategy(_policy));
        }
    }

上述自定义实现执行策略不保证一定有用或许也是一种解决方案呢。

总结

本节我们介绍了强大的Polly库和其对应使用的两种实际场景,有此轮子我们何不用起,将其进行封装可以用于一切重试、缓存、异常等处理。 

我的博客即将入驻“”,诚邀技术同仁一同入驻。
时间: 2024-07-31 00:25:02

已被.NET基金会认可的弹性和瞬态故障处理库Polly介绍的相关文章

Apache 软件基金会决定结束其 Apache C++ 标准库项目

&http://www.aliyun.com/zixun/aggregation/37954.html">nbsp;   Apache 软件基金会决定结束其 Apache C++ 标准库项目 -- stdcxx.该项目远没有 GCC 的 libstdc++ 甚至是 LLVM 的 libc++ 使用广泛.自动 2005 开始 Apache 一直在支持 stdcxx 的开发.这是一个 ISO/IEC 14882 C++ 标准的免费开源实现库,这是在 Rogue Wave 软件开源了其商

Google 宣布已加入 .NET 基金会

微软今天在纽约举办其年度的 Connect() 开发者大会.Google 宣布加入了它的. NET 基金会.微软在 2014 年成立 .NET 基金会管理和引导开源 .NET 开发. Google 还将加入到基金会的技术指导小组,小组的其他成员来自 Red Hat.Unity.三星.JetBrains 以及微软.Google的 Cloud Platform 允许开发者部署 .NET 应用程序,搜索巨人也是活跃的 .NET 开源贡献者.Google加入 .NET 基金会并不令人感到意外,但看到两家

Blackberry 10还未上市 安全性能已获美官方认可

http://www.aliyun.com/zixun/aggregation/16400.html">BlackBerry 10能否收复失地,只有等到明年上市之后才能知道 TechWeb配图 [TechWeb报道]11月8日消息,据国外媒体报道,黑莓制造商RIM周四宣布,美国国家标准与技术研究所(National Institute of Standard and Technology)日前授予BlackBerry 10平台FIPS 140-2认证,这意味着该操作系统的安全性能达到了政府

Asp.Net Core API网关Ocelot

首先,让我们简单了解下什么是API网关?       API网关是一个服务器,是系统的唯一入口.从面向对象设计的角度看,它与外观模式类似.API网关封装了系统内部架构,为每个客户端提供一个定制的API.它可能还具有其它职责,如身份验证.监控.负载均衡.缓存.请求分片与管理.静态响应处理.    API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能.通常,网关也是提供REST/HTTP的访问API.服务端通过API-GW注册和管理服务. 其次,我

听说这篇文章搜罗了编程界最牛的12位大神

简评:是的,你没有看错,本文中出现的每个人都是不折不扣的大神,每一个都战力爆表.他们是程序界的先驱,他们所做出的贡献或多或少地影响着我们的生活,被世人瞻仰.当然,这篇文章也不是纯粹的供人敬仰膜拜,小编则是希望这些大神能成为我们普通人学习的榜样,奋斗的目标.也许在20年后,不懈努力刻苦学习的你也能出现在这份榜单中,成为大神俱乐部中一员呢?谁知道呢? 在这篇文章里面,我们将会看到 12 位空前最伟大的程序员(排名不分先后),下面就开始吧! 1. Dennis Ritchie  Dennis MacA

编程界十二位祖师爷级别的 “大牛”

每位程序员或多或少都为这个社会贡献着什么东西,然而有些程序员的贡献却超过了一个普通人一辈子能奉献的力量,他们是先驱,是伟大的,值得我们致敬. 我们常常会把那些贡献者为使我们生活变得更便利而做出的贡献看作是理所当然的,与那些给我们带来帮助的人相比,上天似乎有一种方式使得它给那些为我们带来娱乐的人的奖励要比前者更多.不过这没关系,我们知道该如何向那些带领着我们程序员和技术爱好者前进的人们致敬. 在这篇文章里面,我们将会看到 12 位空前最伟大的程序员(排名不分先后),下面就开始吧! 1. Denni

新浪微博应对弹性扩容的架构演进

本文讲的是新浪微博应对弹性扩容的架构演进[编者的话]本文结合架构图和数据图,详细介绍了LNMP服务的Docker化,如何制作PHP服务相关镜像,最后结合DCP平台完成PHP服务的首次部署.配置更改.代码上线等.目前,新浪微博主站TV视频站.头条问答.话题.红包飞.通行证等LNMP项目已全量部署,方便弹性扩容.同时,也将继续推进PC主站服务的部署. [3 天烧脑式 Docker 训练营 | 上海站]随着Docker技术被越来越多的人所认可,其应用的范围也越来越广泛.本次培训我们理论结合实践,从Do

又一个项目开源,阿里已成为中国开源的关键力量?

今天,开源软件托管平台github上的阿里巴巴主页又增加了一个新项目:Atlas.Atlas意指巨人,它是Google闻名遐迩的波士顿机器人的外号,也是手机淘宝团队的移动容器化框架的代号.在去年的云栖大会上,阿里巴巴资深技术专家倪生华宣布将于2017年初开源Atlas,现在这个承诺终于兑现了. Atlas开源解决了移动开发者什么痛点? 2013年马云发表公开信宣布阿里巴巴"All in无线"战略,之后手机淘宝App的业务规模也迎来了爆发式增长,据阿里巴巴所公布的数据显示,2016年第4

曾经风靡一时的等离子电视,如今在主流家电市场已难觅踪迹

曾经风靡一时的等离子电视,如今在主流家电市场已难觅踪迹.去年底,有"等离子之父"之称的松下宣布将在2014年3月完全结束其等离子业务.等离子电视市场日渐衰落,全球市场占有率已不足6%,目前仅剩三星.LG和长虹仍在坚守,长虹也是国内唯一拥有等离子面板生产线的企业. 随着等离子阵营日益萎缩,等离子技术的未来变得扑朔迷离.<每日经济新闻>记者采访发现,"坚守者"四川长虹斥巨资打造的等离子面板项目连年亏损,有消息称,该项目目前已在主动控制产量.同时,四川长虹集团