• net6 polly 故障处理组件简单介绍+ 实战封装


    1 polly 主要是故障处理组件

       Polly是一个开源的弹性跟瞬态故障处理类库。它可以在你的程序出现故障,超时,或者返回值达成某种条件的时候进行多种策略处理,比如重试、超时、降级、熔断等等。

    源码地址:https://github.com/App-vNext/Polly

    1.1.超时策略

    1.2重试策略

    1.3降级策略

    1.4熔断策略

    1.5策略包裹(多重策略组装)

    创建一个控制台 一个webapi  控制调用api 

     在控制台引入nuget 包

    Install-Package Polly

    webapi 项目创建几个接口 如下
    // 定义超时调用的APi
    app.MapGet("/api/polly/timeout", () =>
    {
        Thread.Sleep(6000);
        return "Polly Timeout";
    });
    // 定义500结果的APi
    app.MapGet("/api/polly/500", (HttpContext context) =>
    {
        context.Response.StatusCode = 500;
        return "fail";
    });
    // 定义/api/user
    app.MapGet("/api/user/1", () =>
    {
        var user = new Entiyt_memlst
        {
            u_id=10000,
             u_nme = "Jason Chen"
        };
        return user;
    });
    

      

    控制台代码如下
    #region 超时策略  设定5秒超时  api 需要6秒返回结果 所以回执行超时
    //var memberJson = await Policy.TimeoutAsync(5, TimeoutStrategy.Pessimistic, (t, s, y) =>
    //{
    //    Console.WriteLine("超时了~");
    //    return Task.CompletedTask;
    //}).ExecuteAsync(async () =>
    //{
    //    // 业务逻辑
    //    using var httpClient = new HttpClient();
    //    httpClient.BaseAddress = new Uri($"http://localhost:5050");
    //    var memberResult = await httpClient.GetAsync("/api/polly/timeout");
    //    memberResult.EnsureSuccessStatusCode();
    //    var json = await memberResult.Content.ReadAsStringAsync();
    //    Console.WriteLine(json);
    //    return json;
    //});
    #endregion
    
    #region Polly 重试策略
    //  重试有2种方式 一种是异常重试 一种是根据状态码重试
    //  1当发生 HttpRequestException 的时候触发 RetryAsync 重试,并且最多重试3次。 
    //var memberJson = await Policy.Handle<HttpRequestException>().RetryAsync(3).ExecuteAsync(async () =>
    //{
    //    Console.WriteLine("重试中.....");
    //    using var httpClient = new HttpClient();
    //    httpClient.BaseAddress = new Uri($"http://localhost:5050");
    //    var memberResult = await httpClient.GetAsync("/member/1001");
    //    memberResult.EnsureSuccessStatusCode();
    //    var json = await memberResult.Content.ReadAsStringAsync();
    //    return json;
    //});
    
    //2使用 Polly 在出现当请求结果为 http status_code 500 的时候进行3次重试。
    //var memberResult = await Policy.HandleResult<HttpResponseMessage>
    //    (x => (int)x.StatusCode == 500).RetryAsync(3).ExecuteAsync(async () =>
    //{
    //    Thread.Sleep(1000);
    //    Console.WriteLine("响应状态码重试中.....");
    //    using var httpClient = new HttpClient();
    //    httpClient.BaseAddress = new Uri($"http://localhost:5050");
    //    var memberResult = await httpClient.GetAsync("/api/polly/500");
    //    return memberResult;
    //});
    #endregion
    
    #region 服务降级
    //首先我们使用 Policy 的 FallbackAsync("FALLBACK") 方法设置降级的返回值。当我们服务需要降级的时候会返回 "FALLBACK" 的固定值。
    //同时使用 WrapAsync 方法把重试策略包裹起来。这样我们就可以达到当服务调用失败的时候重试3次,如果重试依然失败那么返回值降级为固定的 "FALLBACK" 值。
    //var fallback = Policy<string>.Handle<HttpRequestException>().Or<Exception>().FallbackAsync("FALLBACK", (x) =>
    //{
    //    Console.WriteLine($"进行了服务降级 -- {x.Exception.Message}");
    //    return Task.CompletedTask;
    //}).WrapAsync(Policy.Handle<HttpRequestException>().RetryAsync(3));
    
    //var memberJson = await fallback.ExecuteAsync(async () =>
    //{
    //    using var httpClient = new HttpClient();
    //    httpClient.BaseAddress = new Uri($"http://localhost:5050");
    //    var result = await httpClient.GetAsync("/api/user/" + 1);
    //    result.EnsureSuccessStatusCode();
    //    var json = await result.Content.ReadAsStringAsync();
    //    return json;
    
    //});
    //Console.WriteLine(memberJson);//  当我们服务需要降级的时候会返回 "FALLBACK" 的固定值
    //if (memberJson != "FALLBACK")
    //{
    //    var member = JsonConvert.DeserializeObject<Entiyt_memlst>(memberJson);
    //    Console.WriteLine($"{member!.u_id}---{member.u_nme}");
    //}
    #endregion
    
    //#region 服务熔断
    ////定义熔断策略
    //var circuitBreaker = Policy.Handle<Exception>().CircuitBreakerAsync(
    //   exceptionsAllowedBeforeBreaking: 2, // 出现几次异常就熔断
    //   durationOfBreak: TimeSpan.FromSeconds(10), // 熔断10秒
    //   onBreak: (ex, ts) =>
    //   {
    //       Console.WriteLine("circuitBreaker onBreak ."); // 打开断路器
    //   },
    //   onReset: () =>
    //   {
    //       Console.WriteLine("circuitBreaker onReset "); // 关闭断路器
    //   },
    //   onHalfOpen: () =>
    //   {
    //       Console.WriteLine("circuitBreaker onHalfOpen"); // 半开 放部分流量试探服务是否正常
    //   }
    //);
    
    //// 定义重试策略
    //var retry = Policy.Handle<HttpRequestException>().RetryAsync(3);
    //// 定义降级策略
    //var fallbackPolicy = Policy<string>.Handle<HttpRequestException>().Or<BrokenCircuitException>()
    //    .FallbackAsync("FALLBACK", (x) =>
    //    {
    //        Console.WriteLine($"进行了服务降级 -- {x.Exception.Message}");
    //        return Task.CompletedTask;
    //    })
    //    .WrapAsync(circuitBreaker.WrapAsync(retry));///  这里这是嵌套策略  折行顺序是先重试  在熔断 最后在降级   
     
    //string memberJsonResult = "";
    
    //do
    //{
    //    memberJsonResult = await fallbackPolicy.ExecuteAsync(async () =>
    //    {
    //        using var httpClient = new HttpClient();
    //        httpClient.BaseAddress = new Uri($"http://localhost:5050");
    //        var result = await httpClient.GetAsync("/api/user/" + 1);
    //        result.EnsureSuccessStatusCode();
    //        var json = await result.Content.ReadAsStringAsync();
    //        return json;
    //    });
    //    Thread.Sleep(1000);
    //} while (memberJsonResult == "FALLBACK");
    
    //if (memberJsonResult != "FALLBACK")
    //{
    //    var member = JsonConvert.DeserializeObject<Entiyt_memlst>(memberJsonResult);
    //    Console.WriteLine($"{member!.u_id}---{member.u_nme}");
    //}
    //#endregion

     包裹策略 效果展示如下:

     2 实际开发中我们不可能一个方法写一次 polly 策略。所以要aop 一下。 这里用了autofac 里面的aop

       需要新建一进程用来调用上面的webapi   新建的类有IUserService  、UserService  (实现接口) CustomPollyPolicyInterceptor (polly 切面逻辑)

    定义 IUserService  

       [Intercept(typeof(CustomPollyPolicyInterceptor))]//表示要polly生效
        public interface IUserService
        {
            [PollyPolicyConfig(FallBackMethod = "UserServiceFallback",//UserServiceFallback 方法名称
                IsEnableCircuitBreaker = true,
                ExceptionsAllowedBeforeBreaking = 3,
                MillisecondsOfBreak = 1000 * 5,
                CacheTTLMilliseconds = 1000 * 20)]
            User AOPGetById(int id);
    
            Task<User> GetById(int id);
        }
    
        public record User(int Id, string Name, string Account, string Password);

    UserService 类如下

            /// <summary>
            /// 【AOP方式定义】根据用户ID查询用户信息
            /// </summary>
            /// <param name="id"></param>
            /// <returns></returns>
            /// <exception cref="NotImplementedException"></exception>
            public User AOPGetById(int id)
            {
                string url = $"http://localhost:5050/api/user/1";
             //   string realUrl = abstractConsulDispatcher.MapAddress(url);
                string content = httpAPIInvoker.HttpInvoke(url);
                var user = JsonConvert.DeserializeObject<User>(content)!;
                return  user;
            }

    autofac  CustomPollyPolicyInterceptor切面逻辑逻辑如下:

        [AttributeUsage(AttributeTargets.Method)]
        public class CustomPollyPolicyInterceptor : Attribute,IInterceptor
        {
            private static ConcurrentDictionary<MethodInfo, AsyncPolicy> policies
            = new ConcurrentDictionary<MethodInfo, AsyncPolicy>();
            private static readonly IMemoryCache memoryCache
              = new MemoryCache(new MemoryCacheOptions());
    
            public void Intercept(IInvocation invocation)
            {
                if (!invocation.Method.IsDefined(typeof(PollyPolicyConfigAttribute), true))
                {
                    // 直接调用方法本身
                    invocation.Proceed();
                }
                else
                {
                    PollyPolicyConfigAttribute pollyPolicyConfigAttribute = invocation.Method.GetCustomAttribute<PollyPolicyConfigAttribute>()!;
                    //一个PollyPolicyAttribute中保持一个policy对象即可
                    //其实主要是CircuitBreaker要求对于同一段代码要共享一个policy对象
                    //根据反射原理,同一个方法的MethodInfo是同一个对象,但是对象上取出来的PollyPolicyAttribute
                    //每次获取的都是不同的对象,因此以MethodInfo为Key保存到policies中,确保一个方法对应一个policy实例
                    policies.TryGetValue(invocation.Method, out AsyncPolicy? policy);
                    //把本地调用的AspectContext传递给Polly,主要给FallbackAsync中使用
                    // 创建Polly上下文对象(字典)
                    Context pollyCtx = new();
                    pollyCtx["invocation"] = invocation;
    
                    lock (policies)//因为Invoke可能是并发调用,因此要确保policies赋值的线程安全
                    {
                        if (policy == null)
                        {
                            policy = Policy.NoOpAsync();//创建一个空的Policy
                            if (pollyPolicyConfigAttribute.IsEnableCircuitBreaker)
                            {
                                policy = policy.WrapAsync(Policy.Handle<Exception>()
                                    .CircuitBreakerAsync(pollyPolicyConfigAttribute.ExceptionsAllowedBeforeBreaking,
                                    TimeSpan.FromMilliseconds(pollyPolicyConfigAttribute.MillisecondsOfBreak),
                                    onBreak: (ex, ts) =>
                                    {
                                        Console.WriteLine($"熔断器打开 熔断{pollyPolicyConfigAttribute.MillisecondsOfBreak / 1000}s.");
                                    },
                                    onReset: () =>
                                    {
                                        Console.WriteLine("熔断器关闭,流量正常通行");
                                    },
                                    onHalfOpen: () =>
                                    {
                                        Console.WriteLine("熔断时间到,熔断器半开,放开部分流量进入");
                                    }));
                            }
                            if (pollyPolicyConfigAttribute.TimeOutMilliseconds > 0)
                            {
                                policy = policy.WrapAsync(Policy.TimeoutAsync(() =>
                                    TimeSpan.FromMilliseconds(pollyPolicyConfigAttribute.TimeOutMilliseconds),
                                    Polly.Timeout.TimeoutStrategy.Pessimistic));
                            }
                            if (pollyPolicyConfigAttribute.MaxRetryTimes > 0)
                            {
                                policy = policy.WrapAsync(Policy.Handle<Exception>()
                                    .WaitAndRetryAsync(pollyPolicyConfigAttribute.MaxRetryTimes, i =>
                                    TimeSpan.FromMilliseconds(pollyPolicyConfigAttribute.RetryIntervalMilliseconds)));
                            }
                            // 定义降级测试 可以不要重试 可以不要限流 可以不要熔断 但需要一个降级策略
                            var policyFallBack = Policy.Handle<Exception>().FallbackAsync((fallbackContent, token) =>
                            {
                                // 必须从Polly的Context种获取IInvocation对象
                                IInvocation iv = (IInvocation)fallbackContent["invocation"];
                                var fallBackMethod = iv.TargetType.GetMethod(pollyPolicyConfigAttribute.FallBackMethod!);
                                var fallBackResult = fallBackMethod!.Invoke(iv.InvocationTarget, iv.Arguments);
                                iv.ReturnValue = fallBackResult;
                                return Task.CompletedTask;
                            }, (ex, t) =>
                            {
                                Console.WriteLine("====================>触发服务降级");
                                return Task.CompletedTask;
                            });
    
                            policy = policyFallBack.WrapAsync(policy);
                            //放入到缓存
                            policies.TryAdd(invocation.Method, policy);
                        }
                    }
    
                    // 是否启用缓存
                    if (pollyPolicyConfigAttribute.CacheTTLMilliseconds > 0)
                    {
                        //用类名+方法名+参数的下划线连接起来作为缓存key
                        string cacheKey = "PollyMethodCacheManager_Key_" + invocation.Method.DeclaringType
                                                                           + "." + invocation.Method + string.Join("_", invocation.Arguments);
                        //尝试去缓存中获取。如果找到了,则直接用缓存中的值做返回值
                        if (memoryCache.TryGetValue(cacheKey, out var cacheValue))
                        {
                            invocation.ReturnValue = cacheValue;
                        }
                        else
                        {
                            //如果缓存中没有,则执行实际被拦截的方法
                            Task task = policy.ExecuteAsync(
                                async (context) => {
                                    invocation.Proceed();
                                    await Task.CompletedTask;
                                },
                                pollyCtx
                            );
                            task.Wait();
    
                            //存入缓存中
                            using var cacheEntry = memoryCache.CreateEntry(cacheKey);
                            {
                                cacheEntry.Value = invocation.ReturnValue;
                                cacheEntry.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMilliseconds(pollyPolicyConfigAttribute.CacheTTLMilliseconds);
                            }
                        }
                    }
                    else//如果没有启用缓存,就直接执行业务方法
                    {
                        Task task = policy.ExecuteAsync(
                                async (context) => {
                                    invocation.Proceed();
                                    await Task.CompletedTask;
                                },
                                pollyCtx
                            );
                        task.Wait();
                    }
                }
            }
        }

    在autofac 里面注册 单例  这样polly 才能触发 熔断机制

                    buider.RegisterType<UserService>().As<IUserService>().SingleInstance().EnableInterfaceInterceptors();
                    buider.RegisterType<CustomPollyPolicyInterceptor>();
  • 相关阅读:
    Android Native Hook技术(一)
    Android Native Hook技术(二)
    Dalvik源码阅读笔记(一)
    Dalvik源码阅读笔记(二)
    Android反调试笔记
    /dev/mem可没那么简单
    jenkins使用邮件功能
    docker 安装 oracle
    jstack
    docker network
  • 原文地址:https://www.cnblogs.com/jasontarry/p/15882025.html
Copyright © 2020-2023  润新知