zoukankan      html  css  js  c++  java
  • 容错处理库Polly使用文档

    Design For Failure
    1. 一个依赖服务的故障不会严重破坏用户的体验。
    2. 系统能自动或半自动处理故障,具备自我恢复能力。

    以下是一些经验的服务容错模式

    • 超时与重试(Timeout and Retry)
    • 限流(Rate Limiting)
    • 熔断器(Circuit Breaker)
    • 舱壁隔离(Bulkhead Isolation)
    • 回退(Fallback)

    如果想详细了解这几种模式可以参考美团技术团队的总结:服务容错模式。我们今天要讲的是,thanks to the community 多谢社区, Polly已经为我们实现了以上全部的功能。Polly是一个C#实现的弹性瞬时错误处理库(resilience and transient-fault-handling library一直觉得这个英文翻译不是很好) 。在Polly中,对这些服务容错模式分为两类:

    • 错误处理fault handling :重试、熔断、回退
    • 弹性应变resilience:超时、舱壁、缓存

    可以说错误处理是当错误已经发生时,防止由于该错误对整个系统造成更坏的影响而设置。而弹性应变,则在是错误发生前,针对有可能发生错误的地方进行预先处理,从而达到保护整个系统的目地。

    Polly 错误处理使用三步曲

    • 定义条件: 定义你要处理的 错误异常/返回结果
    • 定义处理方式 : 重试,熔断,回退
    • 执行
    var policy = Policy
                  .Handle<SomeExceptionType>()   // 定义条件
                  .Retry(); // 定义处理方式
     
    // 执行
    policy.Execute(() => DoSomething());

    定义条件

    我们可以针对两种情况来定义条件:错误异常和返回结果。

    // 单个异常类型
    Policy
      .Handle<HttpRequestException>()
     
    // 限定条件的单个异常
    Policy
      .Handle<SqlException>(ex => ex.Number == 1205)
     
    // 多个异常类型
    Policy
      .Handle<HttpRequestException>()
      .Or<OperationCanceledException>()
     
    // 限定条件的多个异常
    Policy
      .Handle<SqlException>(ex => ex.Number == 1205)
      .Or<ArgumentException>(ex => ex.ParamName == "example")
     
    // Inner Exception 异常里面的异常类型
    Policy
      .HandleInner<HttpRequestException>()
      .OrInner<OperationCanceledException>(ex => ex.CancellationToken != myToken)

    以及用返回结果来限定

    // 返回结果加限定条件
    Policy
      .HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.NotFound)
     
    // 处理多个返回结果
    Policy
      .HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError)
      .OrResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.BadGateway)
     
    // 处理元类型结果 (用.Equals)
    Policy
      .HandleResult<HttpStatusCode>(HttpStatusCode.InternalServerError)
      .OrResult<HttpStatusCode>(HttpStatusCode.BadGateway)
      
    // 在一个policy里面同时处理异常和返回结果。
    HttpStatusCode[] httpStatusCodesWorthRetrying = {
       HttpStatusCode.RequestTimeout, // 408
       HttpStatusCode.InternalServerError, // 500
       HttpStatusCode.BadGateway, // 502
       HttpStatusCode.ServiceUnavailable, // 503
       HttpStatusCode.GatewayTimeout // 504
    };
    HttpResponseMessage result = Policy
      .Handle<HttpRequestException>()
      .OrResult<HttpResponseMessage>(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode))
      .RetryAsync(...)
      .ExecuteAsync( /* some Func<Task<HttpResponseMessage>> */ )

    定义处理方式

    在这里使用的处理方式就是我们最开始说的服务容错模式,我们将介绍以下三种:重试、熔断、回退。

    重试

    重试很好理解,当发生某种错误或者返回某种结果的时候进行重试。Polly里面提供了以下几种重试机制

    • 按次数重试
    • 不断重试(直到成功)
    • 等待之后按次数重试
    • 等待之后不断重试(直到成功)

    按次数重试

    // 重试1次
    Policy
      .Handle<SomeExceptionType>()
      .Retry()
     
    // 重试3(N)次
    Policy
      .Handle<SomeExceptionType>()
      .Retry(3)
     
    // 重试多次,加上重试时的action参数
    Policy
        .Handle<SomeExceptionType>()
        .Retry(3, (exception, retryCount) =>
        {
            // 干点什么,比如记个日志之类的
        });

    不断重试

    // 不断重试,直到成功
    Policy
      .Handle<SomeExceptionType>()
      .RetryForever()
     
    // 不断重试,带action参数在每次重试的时候执行
    Policy
      .Handle<SomeExceptionType>()
      .RetryForever(exception =>
      {
            // do something      
      });

    等待之后重试

    // 重试3次,分别等待1、2、3秒。
    Policy
      .Handle<SomeExceptionType>()
      .WaitAndRetry(new[]
      {
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(2),
        TimeSpan.FromSeconds(3)
      });

    当然也可以在每次重试的时候添加一些处理,这里我们可以从上下文中获取一些数据,这些数据在policy启动执行的时候可以传进来。

    Policy
      .Handle<SomeExceptionType>()
      .WaitAndRetry(new[]
      {
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(2),
        TimeSpan.FromSeconds(3)
      }, (exception, timeSpan, context) => {
        // do something   
      });

    熔断

    熔断也可以被作为当遇到某种错误场景下的一个操作。以下代码展示了当发生2次SomeExceptionType的异常的时候则会熔断1分钟,该操作后续如果继续尝试执行则会直接返回错误 。

    Policy
        .Handle<SomeExceptionType>()
        .CircuitBreaker(2, TimeSpan.FromMinutes(1));

    可以在熔断和恢复的时候定义委托来做一些额外的处理。onBreak会在被熔断时执行,而onReset则会在恢复时执行。

    
    

    熔断器状态

    我们的CircuitBreakPolicy的State定义了当前熔断器的状态,我们也可能调用它的Is

    Action<Exception, TimeSpan> onBreak = (exception, timespan) => { ... };
    Action onReset = () => { ... };
    CircuitBreakerPolicy breaker = Policy
        .Handle<SomeExceptionType>()
        .CircuitBreaker(2, TimeSpan.FromMinutes(1), onBreak, onReset);

    Isolate和Reset方法来手动熔断和恢复 。

    CircuitState state = breaker.CircuitState;
    • Closed 关闭状态,允许执行
    • Open 自动打开,执行会被阻断
    • Isolate 手动打开,执行会被阻断
    • HalfOpen  从自动打开状态恢复中,在熔断时间到了之后从Open状态切换到Closed
    // 手动打开熔断器,阻止执行
    breaker.Isolate();
    // 恢复操作,启动执行
    breaker.Reset();

    回退(Fallback)

    // 如果执行失败则返回UserAvatar.Blank
    Policy
       .Handle<Whatever>()
       .Fallback<UserAvatar>(UserAvatar.Blank)
     
    // 发起另外一个请求去获取值
    Policy
       .Handle<Whatever>()
       .Fallback<UserAvatar>(() => UserAvatar.GetRandomAvatar()) // where: public UserAvatar GetRandomAvatar() { ... }
     
    // 返回一个指定的值,添加额外的处理操作。onFallback
    Policy
       .Handle<Whatever>()
       .Fallback<UserAvatar>(UserAvatar.Blank, onFallback: (exception, context) =>
        {
            // do something
        });

    执行polly policy

    为我声明了一个Policy,并定义了它的异常条件和处理方式,那么接下来就是执行它。执行是把我们具体要运行的代码放到Policy里面。

    // 执行一个Action
    var policy = Policy
                  .Handle<SomeExceptionType>()
                  .Retry();
     
    policy.Execute(() => DoSomething());
    // 看我们在retry重试时被调用的一个委托,它可以从context中拿到我们在execute的时候传进来的参数 。
    var policy = Policy
        .Handle<SomeExceptionType>()
        .Retry(3, (exception, retryCount, context) =>
        {
            var methodThatRaisedException = context["methodName"];
            Log(exception, methodThatRaisedException);
        });
     
    policy.Execute(
        () => DoSomething(),
        new Dictionary<string, object>() {{ "methodName", "some method" }}
    );
    Policy
      .Handle<SqlException>(ex => ex.Number == 1205)
      .Or<ArgumentException>(ex => ex.ParamName == "example")
      .Retry()
      .Execute(() => DoSomething());
    protected async Task<T> ExecuteDb<T>(IServiceExecuteContext context, Func<Task<T>> action, string actionName = null, bool retryForever = false)
            {
                RetryPolicy policy = null;
                var policyBuilder = Policy
                    .Handle<SqlException>(ex => ex.Number == 1205); // Handle Deadlock Retry
    
                if (retryForever)
                {
                    policy = policyBuilder.WaitAndRetryForeverAsync((time, retryCtx) => TimeSpan.FromMilliseconds(time * 1000), (ex, time, retryCtx) =>
                    {
                        if (time.TotalSeconds > 30)
                        {
                            Log.Error($"{context.GetCurrentLogMessagePrefix()} {actionName}发生死锁正在重试(等待{time.TotalSeconds}秒)(已重试超过30次!!!!请引起重视!!!!)");
                        }
                        else
                        {
                            Log.Warn($"{context.GetCurrentLogMessagePrefix()} {actionName}发生死锁正在重试(等待{time.TotalSeconds}秒)");
                        }
                    });
                }
                else
                {
                    policy = policyBuilder.WaitAndRetryAsync(new[]
                    {
                        TimeSpan.Zero,
                        TimeSpan.FromMilliseconds(1 * 1000),
                        TimeSpan.FromMilliseconds(2 * 1000)
                    }, (ex, time, retryCtx) =>
                    {
                        Log.Warn($"{context.GetCurrentLogMessagePrefix()} {actionName}发生死锁正在重试(等待{time.TotalSeconds}秒)");
                    });
                }
    
                return await policy.ExecuteAsync(async () => await action());
            }

    摘自 :https://www.cnblogs.com/jesse2013/p/polly-docs.html

  • 相关阅读:
    C#输出JS代码封装类Alart
    我的汇编学习之路(2)win8(64位)下安装debug
    .NET使用一般处理程序生成验证码
    ?运算符(null合并运算符)和三木运算符
    讲解:小菜鸟自制的.NET实体类生成器
    我的汇编学习之路(1)进制转换
    未来
    callee,caller区别
    string::size_type
    ubuntu 12.04 LTS u盘安装
  • 原文地址:https://www.cnblogs.com/shy1766IT/p/9512863.html
Copyright © 2011-2022 走看看