熔断、降级

1、熔断的定义:

熔断就是“保险丝”。当出现某些状况时,切断服务,从而防止应用程序不断地尝试执 
行可能会失败的操作给系统造成“雪崩”,或者大量的超时等待导致系统卡死。 

2、降级目的:

降级的目的是当某个服务提供者发生故障的时候,向调用方返回一个错误响应或者替代 
响应。举例子:调用联通接口服务器发送短信失败之后,改用移动短信服务器发送,如果移 
动短信服务器也失败,则改用电信短信服务器,如果还失败,则返回“失败”响应;在从推 
荐商品服务器加载数据的时候,如果失败,则改用从缓存中加载,如果缓存中也加载失败, 
则返回一些本地替代数据。 

一、创建一个PollyTest(.netcore)控制台项目

1.1、在该项目中添加Nuget包(Polly)

1.2、在Program类中进行测试

1.2.1 异常捕获

            //实例化
            Policy policy = Policy
           .Handle<ArgumentException>() //故障 
           .Fallback(() =>//动作 
           {
               Console.WriteLine("执行出错");
           });
            policy.Execute(() =>
            {
                 //在策略中执行业务代码//这里是可能会产生问题的业务系统代码 
                 Console.WriteLine("开始任务");
                throw new ArgumentException("Hello world!");
                Console.WriteLine("完成任务");
            });
            Console.ReadKey();

1.2.2 重载异常

            //实例化
            Policy policy = Policy
              .Handle<ArgumentException>() //故障 
              .Fallback(() =>//动作 
              {
                  Console.WriteLine("执行出错");
              }, ex =>
              {
                  Console.WriteLine(ex);
              });
            policy.Execute(() =>
            {
                Console.WriteLine("开始任务");
                throw new ArgumentException("Hello world!");
                Console.WriteLine("完成任务");
            });
            Console.ReadKey();

1.2.3 带返回值

            //实例化
            Policy<string> policy = Policy<string>
               .Handle<Exception>() //故障 
               .Fallback(() =>//动作 
               {
                   Console.WriteLine("执行出错");
                   return "降级的值";
               });
            string value = policy.Execute(() =>
            {
                Console.WriteLine("开始任务");
                throw new Exception("Hello world!");
                Console.WriteLine("完成任务");
                return "正常的值";
            });
            Console.WriteLine("返回值:" + value);

1.2.4 永久重试

            //实例化
            Policy policy = Policy
              .Handle<Exception>()
              .RetryForever();
            policy.Execute(() =>
            {
                Console.WriteLine("开始任务");
                if (DateTime.Now.Second % 10 != 0)
                {
                    throw new Exception("出错");
                }
                Console.WriteLine("完成任务");
            });

1.2.5  短路保护

             Policy policy = Policy
            .Handle<Exception>()
            .CircuitBreaker(6, TimeSpan.FromSeconds(5));//连续出错6次之后熔断5秒(不会再去尝试执行业务代码)。 
            while (true)
            {
                Console.WriteLine("开始Execute");
                try
                {
                    policy.Execute(() =>
                    {
                        Console.WriteLine("开始任务");
                        throw new Exception("出错");
                        Console.WriteLine("完成任务");
                    });
                }
                catch (Exception ex)
                {
                    Console.WriteLine("execute出错" + ex);
                }
                Thread.Sleep(500);
            }

1.2.6  超时处理

               Policy policy = Policy
              .Handle<Exception>() //定义所处理的故障
              .Fallback(() =>
              {
                  Console.WriteLine("执行出错");
              });
            policy = policy.Wrap(Policy.Timeout(2, TimeoutStrategy.Pessimistic));
            policy.Execute(() =>
            {
                Console.WriteLine("开始任务");
                Thread.Sleep(5000);
                Console.WriteLine("完成任务");
            }); 

 

二、AOP 框架基础

2.1、在PollyTest项目中添加Nuget包(AspectCore.Core)

2.2、在PollyTest项目中新建一个编写拦截器CustomInterceptorAttribute类

​
​
 public class CustomInterceptorAttribute : AbstractInterceptorAttribute
    {
        //每个被拦截的方法中执行 
        public async override Task Invoke(AspectContext context, AspectDelegate next)
        {
            try
            {
                Console.WriteLine("Before service call");
                await next(context);//执行被拦截的方法 
            }
            catch (Exception)
            {
                Console.WriteLine("Service threw an exception!");
                throw;
            }
            finally
            {
                Console.WriteLine("After service call");
            }
        }
    }

​

​

2.3、在PollyTest项目中新建一个需要被代理拦截的Person类

  public class Person
    {
        [CustomInterceptor]
        public virtual void Say(string msg)
        {
            Console.WriteLine("service calling..." + msg);
        }  
    }

2.4、在PollyTest项目下Program类中创建代理对象

            //通过 AspectCore 创建代理对象
            ProxyGeneratorBuilder proxyGeneratorBuilder = new ProxyGeneratorBuilder();
            using (IProxyGenerator proxyGenerator = proxyGeneratorBuilder.Build())
            {
                Person p = proxyGenerator.CreateClassProxy<Person>();
                p.Say("rupeng.com");
            }
            Console.ReadKey();

 

三、创建简单的熔断降级框架

3.1、在PollyTest项目下Person类中添加代码

        //创建简单的熔断降级框架
        [HystrixCommand("TengXunAsync")]
        public virtual async Task<string> XiaoMiAsync(string name)
        {
            throw new Exception("周瑜!");
            Console.WriteLine("小米短信" + name);
            return "fail";
        }

        [HystrixCommand("HuaWeiAsync")]
        public virtual async Task<string> TengXunAsync(string name)
        {
            throw new Exception("周瑜!");
            Console.WriteLine("腾讯短信" + name);
            return "ok";
        }

        public async Task<string> HuaWeiAsync(string name)
        {
            Console.WriteLine("华为短信" + name);
            return "ok";
        }

3.2、在PollyTest项目新建一个HystrixCommandAttribute类

 [AttributeUsage(AttributeTargets.Method)]
    public class HystrixCommandAttribute : AbstractInterceptorAttribute
    {
        public HystrixCommandAttribute(string fallBackMethod)
        {
            this.FallBackMethod = fallBackMethod;//失败要调用的方法
        }
        public string FallBackMethod { get; set; }
        public override async Task Invoke(AspectContext context, AspectDelegate next)
        {
            try
            {
                await next(context);//执行被拦截的方法
            }
            catch (Exception ex)
            {
                //context.ServiceMethod 被拦截的方法。context.ServiceMethod.DeclaringType被拦截方法所在的类
                //context.Implementation 实际执行的对象 p
                //context.Parameters 方法参数值
                //如果执行失败,则执行 FallBackMethod

                //根据参数字符串获取方法名
                var fallBackMethod =context.ServiceMethod.DeclaringType.GetMethod(this.FallBackMethod);
                //反射调用方法
                Object fallBackResult = fallBackMethod.Invoke(context.Implementation,
                context.Parameters);
                context.ReturnValue = fallBackResult;
            }
        }
    }

3.3、在PollyTest项目下Program类中创建代理对象

            //通过 AspectCore 创建代理对象
            ProxyGeneratorBuilder proxyGeneratorBuilder = new ProxyGeneratorBuilder();
            using (IProxyGenerator proxyGenerator = proxyGeneratorBuilder.Build())
            {
                Person p = proxyGenerator.CreateClassProxy<Person>();
                p.XiaoMiAsync("Hello");
            }
            Console.ReadKey();

 

四、细化框架

4.1、在PollyTest项目中添加Nuget包(Microsoft.Extensions.Caching.Memory)

4.2、在PollyTest项目下HystrixCommandAttribute类中添加代码

 public class HystrixCommandAttribute : AbstractInterceptorAttribute
    {
        public HystrixCommandAttribute(string fallBackMethod)
        {
            this.FallBackMethod = fallBackMethod;//失败要调用的方法
        }

        public HystrixCommandAttribute(string fallBackMethod,int MaxRetryTimes)
        {
            this.FallBackMethod = fallBackMethod;//失败要调用的方法
            this.MaxRetryTimes = MaxRetryTimes;
        }
        public string FallBackMethod { get; set; }       


        /// <summary>
        /// 最多重试几次,如果为0则不重试
        /// </summary>
        public int MaxRetryTimes { get; set; } = 0;
        /// <summary>
        /// 重试间隔的毫秒数
        /// </summary>
        public int RetryIntervalMilliseconds { get; set; } = 100;
        /// <summary>
        /// 是否启用熔断
        /// </summary>
        public bool EnableCircuitBreaker { get; set; } = false;
        /// <summary>
        /// 熔断前出现允许错误几次
        /// </summary>
        public int ExceptionsAllowedBeforeBreaking { get; set; } = 3;
        /// <summary>
        /// 熔断多长时间(毫秒)
        /// </summary>
        public int MillisecondsOfBreak { get; set; } = 1000;
        /// <summary>
        /// 执行超过多少毫秒则认为超时(0表示不检测超时)
        /// </summary>
        public int TimeOutMilliseconds { get; set; } = 0;
        /// <summary>
        /// 缓存多少毫秒(0表示不缓存),用“类名+方法名+所有参数ToString拼接”做缓存Key
        /// </summary>
        public int CacheTTLMilliseconds { get; set; } = 0;
        private IAsyncPolicy policy;
        private static readonly Microsoft.Extensions.Caching.Memory.IMemoryCache memoryCache
        = new Microsoft.Extensions.Caching.Memory.MemoryCache(new
        Microsoft.Extensions.Caching.Memory.MemoryCacheOptions());
        / <summary>
        /
        / </summary>
        / <param name="fallBackMethod">降级的方法名</param>
        //public HystrixCommandAttribute(string fallBackMethod)
        //{
        //    this.FallBackMethod = fallBackMethod;
        //}
        //public string FallBackMethod { get; set; }
        public override async Task Invoke(AspectContext context, AspectDelegate next)
        {
            //一个HystrixCommand中保持一个policy对象即可
            //其实主要是CircuitBreaker要求对于同一段代码要共享一个policy对象
            //根据反射原理,同一个方法就对应一个HystrixCommandAttribute,无论几次调用,
            //而不同方法对应不同的HystrixCommandAttribute对象,天然的一个policy对象共享
            //因为同一个方法共享一个policy,因此这个CircuitBreaker是针对所有请求的。
            //Attribute也不会在运行时再去改变属性的值,共享同一个policy对象也没问题
            lock (this)//因为Invoke可能是并发调用,因此要确保policy赋值的线程安全
            {
                if (policy == null)
                {
                    policy = Policy.NoOpAsync();//创建一个空的Policy
                    if (EnableCircuitBreaker)
                    {
                        policy =
                        policy.WrapAsync(Policy.Handle<Exception>().CircuitBreakerAsync(ExceptionsAllowedBeforeBreaking,
                        TimeSpan.FromMilliseconds(MillisecondsOfBreak)));
                    }
                    if (TimeOutMilliseconds > 0)
                    {
                        policy = policy.WrapAsync(Policy.TimeoutAsync(() =>
                        TimeSpan.FromMilliseconds(TimeOutMilliseconds), Polly.Timeout.TimeoutStrategy.Pessimistic));
                    }
                    if (MaxRetryTimes > 0)
                    {
                        policy =
                        policy.WrapAsync(Policy.Handle<Exception>().WaitAndRetryAsync(MaxRetryTimes, i =>
                        TimeSpan.FromMilliseconds(RetryIntervalMilliseconds)));
                    }
                    IAsyncPolicy policyFallBack = Policy
                   .Handle<Exception>()
                   .FallbackAsync(async (ctx, t) =>
                   {
                       //这里拿到的就是ExecuteAsync(ctx => next(context), pollyCtx);这里传的pollyCtx
                       AspectContext aspectContext = (AspectContext)ctx["aspectContext"];
                       var fallBackMethod =context.ServiceMethod.DeclaringType.GetMethod(this.FallBackMethod);
                       Object fallBackResult = fallBackMethod.Invoke(context.Implementation,context.Parameters);
                       //不能如下这样,因为这是闭包相关,如果这样写第二次调用Invoke的时候context指向的
                       //还是第一次的对象,所以要通过Polly的上下文来传递AspectContext
                       //context.ReturnValue = fallBackResult;
                       aspectContext.ReturnValue = fallBackResult;
                   }, async (ex, t) => { });
                    policy = policyFallBack.WrapAsync(policy);
                }
            }
            //把本地调用的AspectContext传递给Polly,主要给FallbackAsync中使用,避免闭包的坑
            Context pollyCtx = new Context();//Context是polly中通过Execute给FallBack、Execute等回调方法传上下文对象使用的
            pollyCtx["aspectContext"] = context;//context是aspectCore的上下文
                                                //Install-Package Microsoft.Extensions.Caching.Memory
            if (CacheTTLMilliseconds > 0)
            {
                //用类名+方法名+参数的下划线连接起来作为缓存key
                string cacheKey = "HystrixMethodCacheManager_Key_" +
                context.ServiceMethod.DeclaringType
                + "." +
                context.ServiceMethod + string.Join("_", context.Parameters);
                //尝试去缓存中获取。如果找到了,则直接用缓存中的值做返回值
                if (memoryCache.TryGetValue(cacheKey, out var cacheValue))
                {
                    context.ReturnValue = cacheValue;
                }
                else
                {
                    //如果缓存中没有,则执行实际被拦截的方法
                    await policy.ExecuteAsync(ctx => next(context), pollyCtx);
                    //存入缓存中
                    using (var cacheEntry = memoryCache.CreateEntry(cacheKey))
                    {
                        cacheEntry.Value = context.ReturnValue;//返回值放入缓存
                        cacheEntry.AbsoluteExpiration = DateTime.Now +
                        TimeSpan.FromMilliseconds(CacheTTLMilliseconds);
                    }
                }
            }
            else//如果没有启用缓存,就直接执行业务方法
            {
                await policy.ExecuteAsync(ctx => next(context), pollyCtx);
            }
        }


        //public override async Task Invoke(AspectContext context, AspectDelegate next)
        //{
        //    try
        //    {
        //        await next(context);//执行被拦截的方法
        //    }
        //    catch (Exception ex)
        //    {
        //        //context.ServiceMethod 被拦截的方法。context.ServiceMethod.DeclaringType被拦截方法所在的类
        //        //context.Implementation 实际执行的对象 p
        //        //context.Parameters 方法参数值
        //        //如果执行失败,则执行 FallBackMethod

        //        //根据参数字符串获取方法名
        //        var fallBackMethod = context.ServiceMethod.DeclaringType.GetMethod(this.FallBackMethod);
        //        //反射调用方法
        //        Object fallBackResult = fallBackMethod.Invoke(context.Implementation,
        //        context.Parameters);
        //        context.ReturnValue = fallBackResult;
        //    }
        //}
    }

4.3、在PollyTest项目下Person类中添加代码

 //创建简单的熔断降级框架
        [HystrixCommand("TengXunAsync",3)]
        public virtual async Task<string> XiaoMiAsync(string name)
        {
            Console.WriteLine("开始发送小米短信"+name);
            throw new Exception("周瑜!");
            Console.WriteLine("小米短信" + name);
            return "fail";
        }

        [HystrixCommand("HuaWeiAsync",3)]
        public virtual async Task<string> TengXunAsync(string name)
        {
            Console.WriteLine("开始发送腾讯短信" + name);
            throw new Exception("周瑜!");
            Console.WriteLine("腾讯短信" + name);
            return "ok";
        }

        public async Task<string> HuaWeiAsync(string name)
        {
            Console.WriteLine("开始发送华为短信" + name);
            return "ok";
        }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
熔断降级是一种在分布式系统中保护服务稳定性和可用性的机制。当某个服务出现故障或响应时间过长时,熔断机制会暂时停止对该服务的调用,并返回一个预先设定的默认值或错误信息,以避免故障的扩散。Spring Cloud中的熔断降级组件Hystrix提供了实现熔断降级的功能。 要使用Hystrix,首先需要在项目的pom.xml文件中添加Hystrix的依赖: ```xml <!-- Hystrix依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> ``` 接下来,可以在需要进行熔断降级的方法上添加`@HystrixCommand`注解,该注解指定了当方法调用失败或超时时的降级处理方法。例如: ```java @Service public class UserService { @HystrixCommand(fallbackMethod = "getDefaultUser") public User getUserById(Long id) { // 调用其他服务获取用户信息 // ... } public User getDefaultUser(Long id) { // 降级处理逻辑,返回默认用户信息 // ... } } ``` 在上述示例中,`getUserById`方法使用了`@HystrixCommand`注解,并指定了降级处理方法`getDefaultUser`。当调用`getUserById`方法失败或超时时,将会调用`getDefaultUser`方法返回默认的用户信息。 除了使用注解方式,还可以通过编程方式实现熔断降级。可以使用Hystrix提供的`HystrixCommand`和`HystrixObservableCommand`类来封装需要进行熔断降级的逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值