Skip to main content

Command Palette

Search for a command to run...

.NetCore 实践——Polly[二]

Updated
2 min read
E

Science is gold.

polly 组件包:

polly 功能包

polly.Extensions.Http 专门针对http的扩展包

Miscrosoft.Extension.Http.Polly 看到这个名字,那么99%是针对官方.net core的扩展包,是HttpClientFactory 的扩展。

polly有下面一些功能:

  1. 失败重试

  2. 服务熔断

  3. 超时处理

  4. 舱壁处理

  5. 缓存策略

  6. 失败降级

  7. 组合策略

舱壁处理 是什么呢?

这个是限流功能,服务定义最大的流量和队列,避免请求量过大而崩溃。

组合策略,就是对上面的功能可以自由组合。

使用步骤

  1. 定于要处理的异常类型和返回值

  2. 定义要处理的工作

  3. 使用定义的策略来执行代码

polly的源代码:

https://github.com/App-vNext/Polly

polly 针对http的扩展包:

https://github.com/App-vNext/Polly.Extensions.Http

适用场景

  1. 服务"失败"是短暂的,可自愈的。

    针对这种http请求,无状态的是非常适用的。

  2. 服务是"幂等"的,重复调用不会产生副作用

    这个幂等可以简单为多次执行,并不会影响到最初达到的效果。(比如说个人查询,查询多次的话,效果是相同的。)

具体场景:

  1. 服务闪断

  2. 部分节点不可用

在使用重试过程中,最好达到下面几个要求:

  1. 设置失败次数

  2. 设置有步长策略的失败等待间隔

  3. 设置降级响应

  4. 设置断路器

前面说过polly 是针对httpClientFactory 的扩展,当监听到AddTransientHttpErrorPolicy错误的时候,那么可以启动对应的策略进行重试,RetryAsync就是polly的扩展,里面设置重试次数20次。

var s = services
    .AddHttpClient("test")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5))
    .AddTransientHttpErrorPolicy(p=>p.RetryAsync(20));

看下AddTransientHttpErrorPolicy,其实个人感觉RetryAsync倒是没有必要去看,看AddTransientHttpErrorPolicy 是为了知道啥时候会触发这个重试,以及知道如何去定义我们自己的Policy。

public static IHttpClientBuilder AddTransientHttpErrorPolicy(
    this IHttpClientBuilder builder, 
    Func<PolicyBuilder<HttpResponseMessage>, IAsyncPolicy<HttpResponseMessage>> configurePolicy)
{
    if (builder == null)
    {
        throw new ArgumentNullException(nameof(builder));
    }

    if (configurePolicy == null)
    {
        throw new ArgumentNullException(nameof(configurePolicy));
    }

    var policyBuilder = HttpPolicyExtensions.HandleTransientHttpError();

    // Important - cache policy instances so that they are singletons per handler.
    var policy = configurePolicy(policyBuilder);

    builder.AddHttpMessageHandler(() => new PolicyHttpMessageHandler(policy));
    return builder;
}

从名字中可以看到HandleTransientHttpError就是处理异常的。

public static PolicyBuilder<HttpResponseMessage> HandleTransientHttpError()
{
  return Policy<HttpResponseMessage>.Handle<HttpRequestException>().OrTransientHttpStatusCode();
}

这里表示HttpRequestException 会触发,或者OrTransientHttpStatusCode一些状态码下会触发,看下OrTransientHttpStatusCode。

public static PolicyBuilder<HttpResponseMessage> OrTransientHttpStatusCode(
  this PolicyBuilder<HttpResponseMessage> policyBuilder)
{
  if (policyBuilder == null)
    throw new ArgumentNullException(nameof (policyBuilder));
  return policyBuilder.OrResult(HttpPolicyExtensions.TransientHttpStatusCodePredicate);
}

查看HttpPolicyExtensions.TransientHttpStatusCodePredicate:

 private static readonly Func<HttpResponseMessage, bool> TransientHttpStatusCodePredicate = (Func<HttpResponseMessage, bool>) (response => response.StatusCode >= HttpStatusCode.InternalServerError || response.StatusCode == HttpStatusCode.RequestTimeout);

这里面表示 HttpStatusCode.InternalServerError(500)和HttpStatusCode.RequestTimeout(408) 会进行重试。
同样可以设置间隔时间进行重试:

services.AddHttpClient("ds")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5))
    .AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(20, i => TimeSpan.FromSeconds(2)));

还可以使用WaitAndRetryForever 表示一直重试,直到成功,看需求。

同样也可以自定义一些状态或者一些情况,做一些事情:

 var reg=services.AddPolicyRegistry();
 reg.Add("retryforever", Policy.HandleResult<HttpResponseMessage>(message =>
 {
     return message.StatusCode == System.Net.HttpStatusCode.Created;
 }).RetryForever());
 services.AddHttpClient("test")
     .AddPolicyHandlerFromRegistry("retryforever");

推荐做法

/// <summary>
/// Polly:Http 重试策略
/// </summary>
/// Usage:services.AddHttpClient<,>().AddPolicyHandler(GetRetryPolicy())
/// <returns></returns>
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode == HttpStatusCode.NotFound)
        .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) // exponential back-off: 2, 4, 8 etc
                                              + TimeSpan.FromMilliseconds(new Random().Next(0, 1000)) // plus some jitter: up to 1 second (将抖动策略添加到重试策略)
        );
}
// ... 可以根据实际情况定义不同的重试策略

// Program.cs中
services.AddHttpClient("test")
    .AddPolicyHandler(GetRetryPolicy())
   // .AddPolicyHandler(xxxxPolicy())
    ;

More from this blog

C# 标准性能测试高级用法(Benchmark)

在 C# 标准性能测试 已经告诉大家如何使用 BenchmarkDotNet 测试性能,本文会告诉大家高级的用法。 建议是创建一个控制台项目用来做性能测试,这个项目要求是 dotnet framework 4.6 以上,建议是 dotnet 7 的版本。使用这个项目引用需要测试的项目,然后在里面写测试的代码。 例如被测试项目有一个类 Foo 里面有一个叫 Lindexidb 的方法,接下来的任务是需要测试这个 Lindexidb 方法的性能 最简单的测试的代码 public class FooP...

Jan 9, 20247 min read

.NetCore 实践——HttpClientFactory[一]

HttpClientFactory介绍 HttpClientFactory 主要有下面的功能: 管理内部HttpMessageHandler 的生命周期,灵活应对资源问题和DNS刷新问题 支持命名话、类型化配置,集中管理配置,避免冲突。 灵活的出站请求管道配置,轻松管理请求生命周期 内置管道最外层和最内层日志记录器,有information 和 Trace 输出 核心对象: HttpClient HttpMessageHandler SocketsHttpHandler De...

Jan 9, 20245 min read

认识 MSBuild - 1

前言 很多人一谈到 MSBuild,脑子里就会出现 “XML”、“只能用 VS 的属性框图形界面操作”、“可定制性和扩展性差” 和 “性能低” 等印象,但实际上这些除了 “XML” 之外完全都是刻板印象:这些人用着 Visual Studio 提供的图形界面,就完全不愿意花个几分钟时间翻翻文档去理解 MSBuild 及其构建过程。 另外,再加上 vcxproj (Visual C++ 项目)的默认 MSBuild 构建文件写得确实谈不上好(默认只能项目粒度并行编译,想要源码级并行编译你得加钱),...

Jan 9, 20245 min read

.NetCore实战——工作单元模式(UnitOfWork):管理好你的事务

工作单元模式有如下几个特性: 1、使用同一上下文 2、跟踪实体的状态 3、保障事务一致性 我们对实体的操作,最终的状态都是应该如实保存到我们的存储中,进行持久化 接下来看一下代码 为了实现工作单元模式,这里定义了一个工作单元的接口 public interface IUnitOfWork : IDisposable { Task<int> SaveChangesAsync(CancellationToken cancellationToken = default); Task<b...

Jan 9, 20242 min read
E

Edward Chu's blog

41 posts