.NET6之MiniAPI(二十四):用Polly重试

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.NET6之MiniAPI(二十四):用Polly重试相关的知识,希望对你有一定的参考价值。

为了保障系统的稳定和安全,在调用三方服务时,可以增加重试和熔断。重试是调用一次失败后再试几试,避免下游服务一次闪断,就把整个链路终止;熔断是为了防止太多的次数的无效访问,导致系统不可知异常。

Polly是独立的重试机制的三方库,这里只说明在使用httpclient时,请求下游api时的重试和熔断。需要引入NuGet包Microsoft.Extensions.Http.Polly。

先看一个简单的重试

using Polly;


var builder = WebApplication.CreateBuilder(args);


builder.Services
    .AddHttpClient("RetryClient", httpclient =>
    
        httpclient.BaseAddress = new Uri("http://localhost:5258");
    )
    .AddTransientHttpErrorPolicy(policyBuilder => policyBuilder.RetryAsync(3));


var app = builder.Build();
//调用httpclient
app.MapGet("/test", async (IHttpClientFactory httpClientFactory) =>

    try
    
        var httpClient = httpClientFactory.CreateClient("RetryClient");
        var content = await httpClient.GetStringAsync("other-api");
        Console.WriteLine(content);
        return "ok";
    
    catch (Exception exc)
    
        if (!Count.Time.HasValue)
        
            Count.Time = DateTime.Now;
        
        return $"exc.Message    【次数:Count.I++】  【Count.Time.Value.ToString("yyyy-MM-dd HH:mm:ss.fffffff")】";
    
);


//被调用的接口,返回状态码500
app.MapGet("/other-api", (ILogger<Program> logger) =>

    logger.LogInformation($"失败:DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffffff")");
    return Results.StatusCode(500);
);
app.Run();


static class Count

    public static int I = 1;


    public static DateTime? Time;

返回的结果:

通过AddTransientHttpErrorPolicy(policyBuilder => policyBuilder.RetryAsync(3));我们让请求进行了3次重试,加上原本的一次,就是4次请求。在红色框时,会发现请求集中在极短的时间内,如果下游服务有故障,可能不会在这么短的时间内自动恢复,更好的做法是:根据重试的次数,来延长(或随机,或自建延时算法)请求的时间,比如:

.AddTransientHttpErrorPolicy(policyBuilder =>
        policyBuilder.WaitAndRetryAsync(3, retryNumber =>
        
            switch (retryNumber)
            
                case 1:
                    return TimeSpan.FromMilliseconds(500);
                case 2:
                    return TimeSpan.FromMilliseconds(1000);
                case 3:
                    return TimeSpan.FromMilliseconds(1500);
                default:
                    return TimeSpan.FromMilliseconds(100);
            
        ));

这时的结果如下,基本按照我们设置的时间来重试的:

还有几种重试策略,如下:

//一直重试
.AddTransientHttpErrorPolicy(policyBuilder => policyBuilder.RetryForeverAsync());
//每2秒重试一次
.AddTransientHttpErrorPolicy(policyBuilder => policyBuilder.WaitAndRetryForeverAsync(retryNumber =>

    Console.WriteLine(retryNumber);
    return TimeSpan.FromSeconds(2);
));
//在5秒内4次请求,如果50%失败,就熔断10秒
.AddTransientHttpErrorPolicy(policyBuilder =>
 policyBuilder.AdvancedCircuitBreakerAsync(0.5d, TimeSpan.FromSeconds(5), 4, TimeSpan.FromSeconds(10)));

熔断是保护服务的手段,在本例中具体用法如下:

builder.Services
    .AddHttpClient("RetryClient", httpclient =>
    
        httpclient.BaseAddress = new Uri("http://localhost:5258");
    )
    .AddTransientHttpErrorPolicy(policyBuilder =>
        policyBuilder.WaitAndRetryAsync(3, retryNumber =>
        
            switch (retryNumber)
            
                case 1:
                    return TimeSpan.FromMilliseconds(500);
                case 2:
                    return TimeSpan.FromMilliseconds(1000);
                case 3:
                    return TimeSpan.FromMilliseconds(1500);
                default:
                    return TimeSpan.FromMilliseconds(100);
            
        ))
    //熔断
    .AddTransientHttpErrorPolicy(policyBuilder =>
  policyBuilder.CircuitBreakerAsync(6, TimeSpan.FromSeconds(30)));

CircuitBreaker控制如果有6次失败的请求,就暂停30秒,具体提示如下:

以上是关于.NET6之MiniAPI(二十四):用Polly重试的主要内容,如果未能解决你的问题,请参考以下文章

.NET6之MiniAPI(二十九):UnitTest

.NET6之MiniAPI(二十一):限流

.NET6之MiniAPI(二十):实体验证FluentValidation

.NET6之MiniAPI(二十二):HttpClient

.NET6之MiniAPI(二十五):Dapper

.NET6之MiniAPI(二十三):Refit