首页 > 其他分享 >9.Polly在NET中的使用,重试、熔断、超时、降级、限流简单用法

9.Polly在NET中的使用,重试、熔断、超时、降级、限流简单用法

时间:2024-02-23 11:36:43浏览次数:23  
标签:Polly return public 重试 限流 var Ok response id

Polly 是一个 .NET 弹性和瞬态故障处理库,允许开发人员以 Fluent 和线程安全的方式来实现重试、断路、超时、隔离、限流和降级策略。 文档: https://gitee.com/hubo/Polly 重试(Retry) 出现故障自动重试,这个是很常见的场景,如:当发生请求异常、网络错误、服务暂时不可用时,就应该重试。许多故障都是短暂的,并且可能在短时间延迟后自我纠正(也许就只是昙花一现)。 熔断(Circuit-breaker) 当系统遇到严重问题时,快速回馈失败比让用户/调用者等待要好,限制系统出错的体量,有助于系统恢复。比如,当我们去调一个第三方的 API,有很长一段时间 API 都没有响应,可能对方服务器瘫痪了。如果我们的系统还不停地重试,不仅会加重系统的负担,还会可能导致系统其它任务受影响。所以,当系统出错的次数超过了指定的阈值,就要中断当前线路,等待一段时间后再继续。 超时(Timeout) 当系统超过一定时间的等待,我们就几乎可以判断不可能会有成功的结果。比如平时一个网络请求瞬间就完成了,如果有一次网络请求超过了 30 秒还没完成,我们就知道这次大概率是不会返回成功的结果了。因此,我们需要设置系统的超时时间,避免系统长时间做无谓的等待。 隔离(Bulkhead Isolation) 当系统的一处出现故障时,可能促发多个失败的调用,很容易耗尽主机的资源(如 CPU)。下游系统出现故障可能导致上游的故障的调用,甚至可能蔓延到导致系统崩溃。所以要将可控的操作限制在一个固定大小的资源池中,以隔离有潜在可能相互影响的操作。 降级(Fallback) 有些错误无法避免,就要有备用的方案。这个就像浏览器不支持一些新的 CSS 特性就要额外引用一个polyfill 一样。一般情况,当无法避免的错误发生时,我们要有一个合理的返回来代替失败。 缓存(Cache) 一般我们会把频繁使用且不会怎么变化的资源缓存起来,以提高系统的响应速度。如果不对缓存资源的调用进行封装,那么我们调用的时候就要先判断缓存中有没有这个资源,有的话就从缓存返回,否则就从资源存储的地方(比如数据库)获取后缓存起来,再返回,而且有时还要考虑缓存过期和如何更新缓存的问题。Polly 提供了缓存策略的支持,使得问题变得简单。 策略包(Policy Wrap) 一种操作会有多种不同的故障,而不同的故障处理需要不同的策略。这些不同的策略必须包在一起,作为一个策略包,才能应用在同一种操作上。这就是文章开头说的 Polly 的弹性,即各种不同的策略能够灵活地组合起来。     安装包: Microsoft.Extensions.Http.Polly 创建两个项目,一个请求(Polly.Request:http://localhost:5034),一个响应(Polly.Response:http://localhost:5258)   第一步: 在Polly.Request项目中添加一个策略类

/// <summary>
/// 所有的策略
/// </summary>
public class ClientPolicy
{
    /// <summary>
    /// 重试
    /// </summary>
    public AsyncRetryPolicy<HttpResponseMessage> RetryPolicy { get; set; }

    /// <summary>
    /// 熔断
    /// </summary>
    public AsyncCircuitBreakerPolicy BreakerPolicy { get; set; }

    /// <summary>
    /// 降级
    /// </summary>
    public AsyncFallbackPolicy<UserInfo> FallbackPolicy { get; set; }

    /// <summary>
    /// 限流
    /// </summary>
    public AsyncRateLimitPolicy RateLimitPolicy { get; set; }

    public ClientPolicy()
    {
        // 重试5次,等待时间是:2 4 8 16 32
        RetryPolicy = Policy.HandleResult<HttpResponseMessage>(p=>!p.IsSuccessStatusCode)
            .WaitAndRetryAsync(5,retryAttempt=>TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));

        // 连续发生了Exception异常之后,就会抛出熔断异常BrokenCircuitException
        // 一分钟出现2次异常则进行熔断,一分钟之后恢复请求
        BreakerPolicy = Policy.Handle<Exception>().CircuitBreakerAsync(2, TimeSpan.FromMinutes(1));
        
        // 如果捕捉到了Exception 异常之后,返回一个UserInfo 实例
        FallbackPolicy =  Policy<UserInfo>.Handle<Exception>()
            .FallbackAsync(new UserInfo());

        // 1秒钟只允许访问两次
        RateLimitPolicy = Policy.RateLimitAsync(2, TimeSpan.FromSeconds(1));

    }
}

 

Program.cs里面进行注册
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddSwaggerGen();

builder.Services.AddSingleton(new ClientPolicy());
builder.Services.AddHttpClient();

// 超时
builder.Services.AddHttpClient("timeoutTest").AddPolicyHandler(
    Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(8)));



var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

 

添加一个控制器RequestController
[ApiController]
[Route("[controller]/[action]")]
public class RequestController:ControllerBase
{
    private readonly ClientPolicy _clientPolicy;
    private readonly IHttpClientFactory _httpClientFactory;


    public RequestController(ClientPolicy clientPolicy, IHttpClientFactory httpClientFactory)
    {
        _clientPolicy = clientPolicy;
        _httpClientFactory = httpClientFactory;
    }

    /// <summary>
    /// 重试
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}")]
    public async Task<ActionResult> TestRetry(int id)
    {
        using var httpClient = _httpClientFactory.CreateClient();
        var response = await _clientPolicy.RetryPolicy.ExecuteAsync(() =>
            httpClient.GetAsync($"http://localhost:5258/Response/TestRetry/{id}"));
        if (response.IsSuccessStatusCode)
        {
            Console.WriteLine($"请求成功,Code={response.StatusCode}");
        }
        else
        {
            Console.WriteLine($"请求失败,Code={response.StatusCode}");
        }

        return Content($"请求完毕:{response.StatusCode}");
    }
    
    
    /// <summary>
    /// 熔断
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}")]
    public async Task<ActionResult> TestBreaker(int id)
    {
        using var httpClient = _httpClientFactory.CreateClient();
        try
        {
            var response = await _clientPolicy.BreakerPolicy.ExecuteAsync(() =>
                httpClient.GetStringAsync($"http://localhost:5258/Response/TestBreaker/{id}"));
            return Ok(response);
        }
        catch (BrokenCircuitException e)
        {
            Console.WriteLine("发生了熔断");
            return Ok("熔断了");
        }
        catch (Exception e)
        {
            Console.WriteLine("发生了异常");
            return Ok("异常了");
        }
       

        
    }
    
    
    /// <summary>
    /// 降级
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}")]
    public async Task<ActionResult> TestFallback(int id)
    {
        using var httpClient = _httpClientFactory.CreateClient();
        var response = await _clientPolicy.FallbackPolicy.ExecuteAsync(async () =>
        {
            var json = await httpClient.GetStringAsync($"http://localhost:5258/Response/TestFallback/{id}");
            return JsonConvert.DeserializeObject<UserInfo>(json);
        });

        return Ok(response);
    }

    /// <summary>
    /// 超时处理器
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}")]
    public async Task<ActionResult> TestTimeout(int id)
    {
        using var httpClient = _httpClientFactory.CreateClient("timeoutTest");
        try
        {
            var response = await httpClient.GetStringAsync($"http://localhost:5258/Response/TestTimeout/{id}");
            return Ok(response);
        }
        catch (TimeoutRejectedException e)
        {
            Console.WriteLine("发生了超时异常");
            return Ok("超时了");
        }
        catch (Exception e)
        {
            Console.WriteLine("发生了异常");
            return Ok("异常了");
        }
    }
    
    /// <summary>
    /// 限流
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}")]
    public async Task<ActionResult> TestRateLimit(int id)
    {
        using var httpClient = _httpClientFactory.CreateClient();
        try
        {
            var response = await _clientPolicy.RateLimitPolicy.ExecuteAsync(async () =>
            {
                var json = await httpClient.GetStringAsync($"http://localhost:5258/Response/TestRateLimit/{id}");
                return JsonConvert.DeserializeObject<UserInfo>(json);
            });
            return Ok(response);
        }
        catch (RateLimitRejectedException e)
        {
            Console.WriteLine($"发生了限流异常:{e.Message}");
        }
        catch (Exception e)
        {
            Console.WriteLine($"发生了异常:{e.Message}");
        }

        return Ok("请求结束");
    }
}

 

Polly.Response项目添加ResponseController控制器
/// <summary>
/// 响应
/// </summary>
[ApiController]
[Route("[controller]/[action]")]
public class ResponseController:ControllerBase
{

    /// <summary>
    /// 重试
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}")]
    public ActionResult TestRetry(int id)
    {
        var randomNumber = new Random().Next(1,100);
        if (id>randomNumber)
        {
            Console.WriteLine("请求成功 200");
            return Ok("请求成功");
        }
        Console.WriteLine("请求失败 400");
        return BadRequest("请求失败");
    }

    /// <summary>
    /// 熔断
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}")]
    public ActionResult TestBreaker(int id)
    {
        var randomNumber = new Random().Next(1,100);
        if (id>randomNumber)
        {
            Console.WriteLine("请求成功 200");
            return Ok(new UserInfo(){Id = 1,NickName = "张三"});
        }

        throw new Exception("发生了异常");
    }
    
    
    /// <summary>
    /// 降级
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}")]
    public ActionResult TestFallback(int id)
    {
        var randomNumber = new Random().Next(1,100);
        if (id>randomNumber)
        {
            Console.WriteLine("请求成功 200");
            return Ok(new UserInfo{Id = 1,NickName = "张三"});
        }
        throw new Exception("发生了异常");
    }
    
    
    /// <summary>
    /// 超时
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}")]
    public ActionResult TestTimeout(int id)
    {
        var randomNumber = new Random().Next(1,10);
        Thread.Sleep(randomNumber*1000);
        return Ok(new UserInfo {Id = id, NickName = "张三"});
    }
    
    
    /// <summary>
    /// 限流
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}")]
    public ActionResult TestRateLimit(int id)
    {
        return Ok(new UserInfo {Id = id, NickName = "张三"});
    }
}

 

标签:Polly,return,public,重试,限流,var,Ok,response,id
From: https://www.cnblogs.com/MingQiu/p/18029134

相关文章

  • Nginx 配置限流
    Nginx配置限流1概述2限制请求速率2.1、正常限流2.2、处理突发流量2.3、设置白名单2.4、limit_req重复3限制连接数4上传/下载速率限制4.1、limit_rate4.2、limit_rate_after4.3、proxy_limit_rate4.4、动态限速4.2、基于变量动态限速1概述限流(RateLimitt......
  • 限流算法
    固定时间窗比如1秒钟限制访问100次,则用1秒作为时间窗,用个计数器,下个时间窗到了就把计数器置0;实现方式可以用一个线程定时1秒钟刷一次,但在某些系统中,可能会有很多个qps拦截器,这样会导致线程数很多,所以也可以改成记录上次时间窗的时间点,每次计数器+1之前算一下时间窗是否超过1秒了......
  • Java 21 虚拟线程如何限流控制吞吐量
    虚拟线程(VirtualThreads)是Java21所有新特性中最为吸引人的内容,它可以大大来简化和增强Java应用的并发性。但是,随着这些变化而来的是如何最好地管理此吞吐量的问题。本文,就让我们看一下开发人员在使用虚拟线程时,应该如何管理吞吐量。在大多数情况下,开发人员不需要自己创建虚拟......
  • 使用Redis的ZSet集合实现接口限流
    背景一般在设计后端接口的时候,一般都会预估接口能承受的最大流量是多少。那么如果瞬时流量超过了接口的承载力,我们就需要考虑接口做限流处理了。限流实际上是指限制系统的输入流量和输出流量已保持系统的稳定性,防止极端条件下系统因为突然的请求激增而造成的崩溃。思考我们知......
  • nginx做白名单和限流
    ​ 在我们生产环境中使用到了地图服务,每个月有免费请求次数,近一个月请求次数突然暴涨,导致直接开启付费模式,一个月上百刀的花销着实难扛,根据实际我们的业务使用情况,远达不到付费标准,故考虑做白名单和限流措施,基于以上情况并遇到春节急需快速处理,所以选择了最简单方便的方式,通过ngin......
  • RocketMQ—RocketMQ消费重试和死信消息
    RocketMQ—RocketMQ消费重试和死信消息消费重试生产者重试设置重试的代码如下//失败的情况重发3次producer.setRetryTimesWhenSendFailed(3);//消息在1S内没有发送成功,就会重试producer.send(msg,1000);一般情况下,我们不会在生产者方进行重试。消费者重试消费者在消......
  • kube-apiserver限流配置
    k8sv1.19.0APF之前限流客户端访问kube-apiserver,限流参数有max-mutating-requests-inflight(默认值是200,对应操作类请求)和max-requests-inflight(默认值是400,对应查询类请求)。staging/src/k8s.io/apiserver/pkg/server/filters/maxinflight.goWithMaxInFlightLimit函数分别创建......
  • 5小步快速集成使用sentinel限流
    在微服务系统中,缓存、限流、熔断是保证系统高可用的三板斧。本文通过如下几个小步骤,即可让spring项目快速集成使用sentinel实现系统限流。1、环境和资源准备sentinel支持许多流控方式,比如:单机限流、熔断降级、集群限流、系统保护规则、黑白名单授权等。本文介绍如何快速集成......
  • zuul实现限流
    zuul限流限流算法漏桶: leakeybucket,原理:桶的下方的小孔会以一个相对恒定的速率漏水,而不管入桶的水流量,这样就达到了控制出水口的流量令牌桶: tokenbucket,原理:以相对恒定的速率向桶中加入令牌,请求来时于桶中取令牌,取到了就放行,没能取到令牌的请求则丢弃限流粒度粗粒......
  • 解决淘宝登录频繁提示,验证失败,点击框体重试(error:9tFhU6)
    1、起因近期淘宝登录需要右划验证,才能点击登录,但是一直提示"验证失败,点击框体重试"类似错误如下图所示2、发现问题通常有这种问题的,大概率是安装了浏览器广告屏蔽插件经核查,我的浏览器安装的是AdGuard看了一下页面的调试日志,发现有一条用来通信的WebSocket协议被插件屏蔽了......