已被.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)

断路器策略针对的前置条件是当系统严重挣扎时,快速失败比让用户/呼叫者等待更好。保护故障系统免受过载,可以帮助它恢复。

超时(Timeout)

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

隔板隔离(Bulkhead Isolation)

隔板隔离针对的前置条件是当进程出现故障时,备份的多个失败的呼叫可以轻松地在主机中对资源(例如线程/ CPU)进行漫游。下游系统故障也可能导致上游“备份”失败的呼叫。这两个风险都是一个错误的过程,导致更广泛的系统。

缓存(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-10-03 21:53:33

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

弹性和瞬态故障处理库Polly

已被.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],你值得拥有

弹性盒属相文档详细介绍

弹性盒属相文档详细介绍 display:flex; 声明本元素是弹性盒容器 如果目标元素是行内元素 使用display:inline-flex; flex-direction 取值 描述 row 默认值 ,弹性盒子元素按X轴方向顺序排列 row-reverse 弹性盒子元素按照X轴风向逆序排列 column 弹性盒子元素按照Y轴方向顺序排列 column-reverse 弹性盒子元素按照Y轴方向逆序排列 flex-wrap 取值 描述 nowrap 默认值,flex子元素只会单行显示,flex子

阿里巴巴已加入Linux基金会

阿里巴巴集团已于本月14日宣布正式加入全球知名非营利性组织Linux基金会,作为Linux基金会中首个来自中国的互联网公司,阿里巴巴 集团对于Linux的帮助表示感谢,并将持续加大对技术的投入以促进Linux的发展.在加入Linux基金会的同时,阿里巴巴也成为开源项目Xen的顾 问委员会成员,Xen是Linux基金会合作项目. Linux基金会官方对于阿里巴巴的加入表示欢迎:"近年来Linux实际上已经成为协同开发的途径.无论是作为容器类的解决方案和虚拟银行,还是轻量级的计算解决方案,本次加入的

弹性布局(Flex)布局介绍

Flex是Flexible Box的缩写,意为"弹性布局".任何一个容器都可以指定为Flex布局,块级元素为display:block,行内元素为display:inline-flex. 注意,设为Flex布局以后,子元素的float.clear和vertical-align属性将失效. 以下是一个实现Flex基本布局的代码: <style> .container{ width: 100%; height: 200px; background-color: white; d

Polly+AspectCore实现熔断与降级机制

Polly+AspectCore实现熔断与降级机制 https://www.cnblogs.com/edisonchou/p/9159644.html 一.熔断.降级与AOP 1.1 啥是熔断? 在广义的解释中,熔断主要是指为控制股票.期货或其他金融衍生产品的交易风险,为其单日价格波动幅度规定区间限制,一旦成交价触及区间上下限,交易则自动中断一段时间("熔即断"),或就此"躺平"而不得超过上限或下限("熔而不断"). 而对于微服务来说,熔断就是我

net core体系-API-1Ocelot-(3)项目实战

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

.NET Core微服务之基于Polly+AspectCore实现熔断与降级机制

一.熔断.降级与AOP 1.1 啥是熔断? 在广义的解释中,熔断主要是指为控制股票.期货或其他金融衍生产品的交易风险,为其单日价格波动幅度规定区间限制,一旦成交价触及区间上下限,交易则自动中断一段时间("熔即断"),或就此"躺平"而不得超过上限或下限("熔而不断"). 而对于微服务来说,熔断就是我们常说的"保险丝",意为当服务出现某些状况时,切断服务,从而防止应用程序不断地常识执行可能会失败的操作造成系统的"雪崩&q

Polly的多种弹性策略介绍和简单使用

什么是Polly? Polly是一个.NET弹性和瞬态故障处理库.允许我们以非常顺畅和线程安全的方式来执行诸如行重试,断路,超时,故障恢复等策略. Polly项目地址:https://github.com/App-vNext/Polly Polly提供多种弹性策略:重试(Retry),断路器(Circuit-breaker),超时检测(Timeout),缓存(Cache),降级(FallBack) 重试(Retry): 前置条件:许多故障是短暂的,并且可能在短暂延迟后自我纠正 政策如何缓解:允许

34 个今年11月最受欢迎的 JavaScript 库

作者:Iren Korkishko 译者:前端小智 来源:dev 点赞再看,养成习惯 本文 GitHub:github.com/qq449245884… 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料.欢迎Star和完善,大家面试可以参照考点复习,希望我们一起有点东西. 直接开门见山,看看有哪些好用受欢迎的库值得我们使用. 1.nodemon GitHub: github.com/remy/nodemo… GitHub Stars: 19.6 k 在编写调试Node.js项