首页 > 其他分享 >.NET CORE微服务之Polly

.NET CORE微服务之Polly

时间:2024-03-12 22:34:02浏览次数:22  
标签:CORE Console 策略 Polly 熔断 WriteLine Policy NET

 

概念

Polly是一个被.net基金会支持认可的框架,Polly是一个.NET弹性和瞬态故障处理库,允许开发人员以流畅和线程安全的方式表达策略,如重试、断路器、超时、舱壁隔离和回退。

Polly的7种策略

Polly是一个.NET Core中的弹性和瞬态故障处理库,它提供了多种策略来处理服务故障,确保系统的健壮性和可用性。以下是Polly的一些主要功能:

  1. 重试策略(Retry):当服务调用失败时,Polly可以自动进行重试,这有助于处理那些可能因为暂时性问题导致的服务不可用情况。
  2. 断路器(Circuit Breaker):当检测到服务连续不可用时,断路器策略会介入,快速返回错误响应,避免对下游服务的持续请求,从而预防服务雪崩现象。
  3. 超时策略(Timeout):为服务调用设置一个最大执行时间,超过这个时间的服务调用将被认为失败,可以采取预设的应对措施。
  4. 舱壁隔离(Bulkhead Isolation):通过限制对服务的并发调用数量,防止因某个服务的问题影响到整个系统的稳定性。
  5. 缓存策略(Cache):提供一种机制,可以在服务调用的结果不变的情况下直接使用缓存结果,减少不必要的服务调用。
  6. 降级策略(Fallback):当服务调用失败时,可以提供一个备用的逻辑或者数据作为响应,提高用户体验。
  7. 策略组合(PolicyWrap) : Polly针对不同的故障有不同的策略,我们可以灵活的组合策略,上述的六种策略可以灵活组合使用。

Polly的策略定义了“故障”和“动作”两个部分,其中“故障”可以包括异常、超时等情况,而“动作”则包括降级、重试、熔断等应对措施。这些策略可以单独使用,也可以组合使用,以适应不同的业务需求和场景。

综上所述,Polly是一个功能丰富的库,它能够帮助开发者在.NET Core微服务架构中实现服务治理,提高系统的弹性和稳定性。通过使用Polly,可以有效地处理服务故障,保证服务的高可用性。

代码部分(整合成了一个类,可直接依赖注入)

1.创建一个ASP.NET CORE Web Api或者ASP.NET CORE MVC项目。

2.Nuget引入Polly程序包。

 3.Polly服务治理工具类。

    /// <summary>
    /// 服务治理
    /// </summary>
    public class PollyHelper
    {
        Policy PolicyExecutes = Policy.Bulkhead(5);
        ISyncPolicy? pollyCircuitBreaker;
        ISyncPolicy? policyFallback;
        ISyncPolicy? policy;

        /// <summary>
        /// 降级策略(回退策略)
        /// </summary>
        /// <param name="actionFallback"></param>
        /// <param name="actionExecute"></param>
        public void PollyFallback(Action actionFallback, Action actionExecute)
        {
            //以下代码声明Policy处理ArgumentException异常,处理异常大动作为降级,policy.Execute()方法是指没有发生异常时,我们的业务逻辑。
            //声明Policy处理ArgumentException异常--当异常类型与定义监控异常类型ArgumentException不匹配的情况下是不会执行Fallback()的
            ISyncPolicy _policy = Polly.Policy.Handle<ArgumentException>().Or<ArgumentException>()
             //出现异常时的动作
             .Fallback(() => { actionFallback.Invoke(); });
            //没有异常时正常正常的业务逻辑
            _policy.Execute(() => { actionExecute.Invoke(); });
        }

        /// <summary>
        /// 重试策略
        /// </summary>
        /// <param name="actionExecute">正常逻辑</param>
        public void PollyRetry(Action actionExecute)
        {
            ISyncPolicy _policy = Polly.Policy.Handle<ArgumentException>().Retry(5);
            //没有异常时正常正常的业务逻辑
            _policy.Execute(() => { actionExecute.Invoke(); });
        }

        /// <summary>
        /// 熔断策略
        /// </summary>
        /// <param name="exceptionsAllowedBeforeBreaking"></param>
        /// <param name="durationOfBreak"></param>
        /// <param name="aonBreak"></param>
        /// <param name="aonReset"></param>
        /// <param name="aonHalfOpen"></param>
        /// <param name="execute"></param>
        public void PollyCircuitBreaker(int exceptionsAllowedBeforeBreaking, TimeSpan durationOfBreak, Action aonBreak, Action aonReset, Action aonHalfOpen, Action actionFallback, Action execute)
        {
            //熔断策略
            if (policy == null)
            {
                pollyCircuitBreaker = Policy.Handle<Exception>()
               .CircuitBreaker(
                   // 熔断前允许出现几次错误
                   exceptionsAllowedBeforeBreaking,
                   // 熔断时间
                   durationOfBreak,
                   // 熔断时触发 OPEN
                   onBreak: (ex, breakDelay) =>
                   {
                       aonBreak.Invoke();
                   },
                   // 熔断恢复时触发  CLOSE
                   onReset: () =>
                   {
                       aonReset.Invoke();
                   },
                   // 熔断时间到了之后触发,尝试放行少量(1次)的请求,
                   onHalfOpen: () =>
                   {
                       aonHalfOpen.Invoke();
                   }
               );
                // 回退策略,降级!
                policyFallback = Policy.Handle<Exception>()
                    .Fallback(() =>
                    {
                        actionFallback.Invoke();
                    }, exception =>
                    {
                        Console.WriteLine($"Fallback异常:{exception.GetType()}");
                    });
                policy = Policy.Wrap(policyFallback, pollyCircuitBreaker);
            }
            policy.Execute(() => { execute.Invoke(); });
        }


        /// <summary>
        /// 超时+回退策略
        /// </summary>
        /// <param name="actionFallback"></param>
        /// <param name="actionExecute"></param>
        public void PollyTimeOut(Action actionFallback, Action actionExecute)
        {
            //超时
            Policy policyTimeout = Policy.Timeout(3, TimeoutStrategy.Pessimistic);
            //降级
            Policy policyFallback = Policy.Handle<Exception>()
                .Fallback(() =>
                {
                    actionFallback.Invoke();
                });
            Policy policy = policyFallback.Wrap(policyTimeout);
            policy.Execute(() =>
            {
                actionExecute.Invoke();
            });
        }

        /// <summary>
        /// 隔离策略
        /// </summary>
        /// <param name="actionExecute"></param>
        public void PollyBulkhead(Action actionExecute)
        {
            //不能在方法中使用Policy,否则失效。因为会重新创建一个Policy。
            PolicyExecutes.Execute(() =>
            {
                actionExecute.Invoke();
            });
        }

        /// <summary>
        /// Polly组合(策略组合)
        /// </summary>
        /// <returns></returns>
        public ISyncPolicy CreatePolly()
        {
            // 超时1秒
            var timeoutPolicy = Policy.Timeout(1, TimeoutStrategy.Pessimistic, (context, timespan, task) =>
            {
                Console.WriteLine("执行超时,抛出TimeoutRejectedException异常");
            });


            // 重试2次
            var retryPolicy = Policy.Handle<Exception>()
                .WaitAndRetry(
                    2,
                    retryAttempt => TimeSpan.FromSeconds(2),
                    (exception, timespan, retryCount, context) =>
                    {
                        Console.WriteLine($"{DateTime.Now} - 重试 {retryCount} 次 - 抛出{exception.GetType()}-{timespan.TotalMilliseconds}");
                    });

            // 连续发生两次故障,就熔断3秒
            var circuitBreakerPolicy = Policy.Handle<Exception>()
                .CircuitBreaker(
                    // 熔断前允许出现几次错误
                    2,
                    // 熔断时间
                    TimeSpan.FromSeconds(5),
                    // 熔断时触发 OPEN
                    onBreak: (ex, breakDelay) =>
                    {
                        Console.WriteLine($"{DateTime.Now} - 断路器:开启状态(熔断时触发)");
                    },
                    // 熔断恢复时触发 // CLOSE
                    onReset: () =>
                    {
                        Console.WriteLine($"{DateTime.Now} - 断路器:关闭状态(熔断恢复时触发)");
                    },
                    // 熔断时间到了之后触发,尝试放行少量(1次)的请求,
                    onHalfOpen: () =>
                    {
                        Console.WriteLine($"{DateTime.Now} - 断路器:半开启状态(熔断时间到了之后触发)");
                    }
                );

            // 回退策略,降级!
            var fallbackPolicy = Policy.Handle<Exception>()
                .Fallback(() =>
                {
                    Console.WriteLine("这是一个Fallback");
                }, exception =>
                {
                    Console.WriteLine($"Fallback异常:{exception.GetType()}");
                });

            // 策略从右到左依次进行调用
            // 首先判断调用是否超时,如果超时就会触发异常,发生超时故障,然后就触发重试策略;
            // 如果重试两次中只要成功一次,就直接返回调用结果
            // 如果重试两次都失败,第三次再次失败,就会发生故障
            // 重试之后是断路器策略,所以这个故障会被断路器接收,当断路器收到两次故障,就会触发熔断,也就是说断路器开启
            // 断路器开启的3秒内,任何故障或者操作,都会通过断路器到达回退策略,触发降级操作
            // 3秒后,断路器进入到半开启状态,操作可以正常执行
            return Policy.Wrap(fallbackPolicy, circuitBreakerPolicy, retryPolicy, timeoutPolicy);
        }
    }

 4.依赖注入

//依赖注入,polly需要注册成单例,因为一但注册成瞬时的每次调用重新生成一个对象,服务策略失效
builder.Services.AddSingleton<PollyHelper>();

5.控制器代码

    [Route("api/[controller]")]
    [ApiController]
    public class PollyController : ControllerBase
    {
        private readonly PollyHelper _pollyHelper;

        //构造函数注入
        public PollyController(PollyHelper pollyHelper)
        {
            _pollyHelper = pollyHelper;
        }

        /// <summary>
        /// 降级策略
        /// </summary>
        /// <returns></returns>
        [HttpGet("Fallback")]
        public void GetFallback()
        {
            _pollyHelper.PollyFallback(() =>
            {
                Console.WriteLine("方法出错,开始降级");
            }, () =>
            {
                Console.WriteLine("开始处理正常业务");
                Console.WriteLine(".................................");
                Console.WriteLine("执行正常业务处理");
                Console.WriteLine(".................................");
                throw new ArgumentException();
                Console.WriteLine("正业务处理完成");
            });
        }

        /// <summary>
        /// 重试策略
        /// </summary>
        /// <returns></returns>
        [HttpGet("Retry")]
        public void GetRetry()
        {
            _pollyHelper.PollyRetry(() =>
            {
                Console.WriteLine("开始处理正常业务");
                Console.WriteLine(".................................");
                Console.WriteLine("执行正常业务处理");
                Console.WriteLine(".................................");
                throw new ArgumentException();
                Console.WriteLine("正业务处理完成");
            });
        }

        /// <summary>
        /// 熔断策略
        /// </summary>
        /// <returns></returns>
        [HttpGet("CircuitBreaker_Ioc")]
        public void GetCircuitBreaker(int a = 0, bool b = false)
        {
            Console.WriteLine($"*****************for*****************");
            for (int i = a; i < 20; i++)
            {
                _pollyHelper.PollyCircuitBreaker(3, TimeSpan.FromSeconds(2), () =>
                {
                    Console.WriteLine("断路器:开启状态(熔断时触发)");
                }, () =>
                {
                    Console.WriteLine("断路器:关闭状态(熔断恢复时触发)");
                }, () =>
                {
                    Console.WriteLine("断路器:半开启状态(熔断时间到了之后触发)");
                }, () =>
                {
                    Console.WriteLine("这是一个Fallback开始降级");
                }, () =>
                {
                    Console.WriteLine($"-------------第{i}次请求-------------");
                    Console.WriteLine("正常情况下:开始执行");
                    Thread.Sleep(1000);
                    if (i < 3) throw new Exception("出错了");
                    Console.WriteLine("正常情况下:执行结束");
                });
            }
            Console.WriteLine($"*****************forend****************");
            //此处是为了等待熔断接回(配置了熔断2秒,这里等一下,要不下次请求还没接上直接报错)
            Thread.Sleep(5000);
            if (b)
            {
                Console.WriteLine($"*****************跳出+{b.ToString()}*****************");
                return;
            }
            GetCircuitBreaker(3, true);
        }

        /// <summary>
        /// 策略封装
        /// </summary>
        /// <returns></returns>
        [HttpGet("Wrap")]
        public void GetWrap()
        {
            //重试3次
            Policy policyRetry = Policy.Handle<Exception>()
                .Retry(3);
            //降级
            Policy policyFallback = Policy.Handle<Exception>()
               .Fallback(() =>
               {
                   Console.WriteLine("降级了");
               });
            //重试三次后降级
            Policy policyWarp = policyFallback.Wrap(policyRetry);
            policyWarp.Execute(() =>
            {
                Console.WriteLine("开始执行");
                for (int i = 0; i < 100; i++)
                {
                    if (i % 2 == 0)
                        throw new Exception("出错了");
                }
                Console.WriteLine("执行结束");
            });
        }

        /// <summary>
        /// 超时策略
        /// </summary>
        /// <returns></returns>
        [HttpGet("Timeout")]
        public void GetTimeout()
        {
            _pollyHelper.PollyTimeOut(() => { Console.WriteLine("降级"); }, () =>
            {
                Console.WriteLine("开始执行");
                Thread.Sleep(5000);//阻塞5秒钟,模拟超时
                Console.WriteLine("执行结束");
            });
        }

        /// <summary>
        /// 隔离策略(Bulkhead Isolation)
        /// </summary>
        /// <returns></returns>
        [HttpGet("Bulkhead")]
        public void GetBulkhead()
        {
            //制定策略最大的并发数为5
            for (int i = 0; i < 6; i++)
            {
                Task.Factory.StartNew(() =>
                {
                    try
                    {
                        _pollyHelper.PollyBulkhead(() => { Console.WriteLine($"********************开始执行程序**************************"); });
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                });
            }
        }

        /// <summary>
        /// Polly测试
        /// </summary>
        /// <returns></returns>
        [HttpGet("Test")]
        public void GetTest()
        {
            var policy = _pollyHelper.CreatePolly();

            for (int i = 0; i < 20; i++)
            {
                Console.WriteLine($"-------------第{i}次请求-------------");
                policy.Execute(() =>
                {
                    // 从10次开始,正常请求成功
                    if (i < 10)
                    {
                        Thread.Sleep(3000);
                    }
                    else
                    {
                        Console.WriteLine($"{DateTime.Now}:请求成功");
                    }
                });
                Thread.Sleep(1000);
            }
        }
    }

总结: 

Polly是一个.NET Core微服务框架,用于实现容错、重试、熔断等策略。它可以帮助开发者在分布式系统中提高可靠性和稳定性。Polly提供了丰富的策略,如重试策略、熔断策略、超时策略等,可以灵活地应用于微服务架构中。通过使用Polly,开发者可以更好地处理异常情况,提高系统的可用性和性能。

标签:CORE,Console,策略,Polly,熔断,WriteLine,Policy,NET
From: https://blog.csdn.net/m0_59178355/article/details/136605237

相关文章

  • 命令行 要查看在Windows上已安装的所有.NET Framework版本 查看在Windows上已安装的
       要查看在Windows上已安装的所有.NETFramework版本,可以按照以下步骤执行:打开命令提示符(CommandPrompt)或PowerShell。可以通过在Windows搜索栏中键入“cmd”或“PowerShell”来找到并打开这些应用程序。在命令提示符或PowerShell中,输入以下命令并按Enter键:......
  • CANopen转Profinet网关连接西门子PLC与变流器通讯
    CANopen转Profinet网关连接西门子PLC与变流器通讯CANopen转Profinet网关(XD-COPNm20)在智能领域,变流器的应用非常广泛,变流器一般会采用CANopen协议。现场采用台达的变流器(支持CANopen协议)作为CANopen从站,S7-1500系列PLC做主站,通过CANopen转Profinet网关(XD-COPNm20)连接变流器与PLC。......
  • kubernetes 安装 kubernetes-dashboard 7.x
    kubernetes安装kubernetes-dashboard7.x介绍Kubernetes仪表板是Kubernetes集群的通用、基于Web的UI。它允许用户管理集群中运行的应用程序并对其进行故障排除,以及管理集群本身。从7.x版开始,不再支持基于Manifest的安装。现在只支持基于Helm的安装。由于多容器设置和对Kong网......
  • 西门子S7.NET通信库【读】操作详解
    在使用西门子PLC进行工业自动化控制的过程中,经常需要与PLC进行数据交换。S7.NET是一款广泛应用于.NET平台的西门子PLC通信库,它为开发者提供了一系列的API函数,以便在C#、VB.NET等.NET语言中轻松实现与西门子PLC的数据交互。本文将详细介绍如何使用S7.NET通信库执行读操作。1......
  • 高dpi下,Vb.net调整控件位置的小经验
     高dpi下,Vb.net调整控件位置的小经验 boy8199/3vdo/club最近写了一个捕快TXT网文采集软件,结果发现在DPI不同的情况下,软件布局会变形.找了半天原因才发现是DPI的问题,默认系统的dpi是96(100%)现在显示器的屏幕比较大,所以好多人会把显示放大到125%或150%导致程序控件变形......
  • C#/.NET/.NET Core拾遗补漏合集(持续更新)
    前言在这个快速发展的技术世界中,时常会有一些重要的知识点、信息或细节被忽略或遗漏。《C#/.NET/.NETCore拾遗补漏》专栏我们将探讨一些可能被忽略或遗漏的重要知识点、信息或细节,以帮助大家更全面地了解这些技术栈的特性和发展方向。GitHub开源地址https://github.com/Y......
  • Unable to cast object of type 'Microsoft.EntityFrameworkCore.Query.Internal.Enti
    如题再做查询的时候报了这个错误。原代码如下:publicvirtualasyncTask<PagedList<ApiScope>>GetApiScopesAsync(stringsearch,intpage=1,intpageSize=10){varpagedList=newPagedList<ApiScope>();varfilteredApiScopes......
  • 基于Vue(提供Vue2/Vue3版本)和.Net Core前后端分离、跨平台的快速开发框架
    前言今天大姚给大家推荐一款基于Vue(提供Vue2/Vue3版本)和.NetCore前后端分离、开源免费(MITLicense)、强大、跨平台的快速开发框架,并且框架内置代码生成器(解决重复性工作,提高开发效率),支持移动端(iOS/Android/H5/微信小程序):Vue.NetCore。提高开发生产效率、避免996可以考虑试试这......
  • 将Asp.net Core 微服务容器部署到 Kubernetes
    将微服务容器部署到KubernetesKubernetes会为你运行容器,需要通过YAML文件描述希望Kubernetes执行的操作,在Kubernetes上部署和运行后端服务简单操作如下步骤安装Kubernetes工具和实现我们需要同时安装kubectl工具和Kubernetes实现按照参考:https://www.cnblogs.co......
  • Error running 'Tomcat 8.5.27': Unable to open debugger port (127.0.0.1:2887): ja
    火绒安全-导致的tomcat8启动异常 一、问题由来最近有个朋友在学习使用IDEA配置tomcat8.5.99的时候,使用一切都正常,直到学习到使用Servlet实现文件下载功能的时候,出现问题。写了一个简单的Servlet用来测试文件下载,直接把路径放在浏览器中测试的时候,可以正常下载。可是不......