在 .NET Core 中结合 HttpClientFactory 使用 Polly(下篇)

本文探讨了Polly和HttpClientFactory在ASP.NET Core中的高级应用,包括如何在重试策略中应用超时,如何利用缓存策略提升性能,以及如何在策略执行中交换信息。这些实践有助于构建更稳定、高效的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

译者序:这是“Polly and HttpClientFactory”这篇Wiki文档翻译的下篇。你可以 点击这里查看上篇,和 点击这里查看中篇。本篇(下篇)主要讲几个Polly和HttpClientFactory在ASP.NET Core中结合使用的用例。如果你对ASP.NET Core 2.1新引入的HttpClient工厂还比较陌生,建议先阅读我的另一篇文章 .NET Core中正确使用 HttpClient的姿势,这有助于更好地理解本文。

—— 正文 ——

下面主要讲几个Polly和HttpClientFactory在ASP.NET Core中结合使用的用例。

用例:应用超时策略

HttpClient已经有了一个Timeout属性,但是在使用重试策略时该如何应用呢?Polly的超时策略又适用于什么地方?

  • HttpClient.Timeout属性设置的超时将被应用于HttpClient实例的所有调用,包括重试之间的所有尝试和等待。

  • 要在每次重试中使用超时,就要在Polly的超时策略之前配置重试策略。

  • 在这种情况下,你可能希望重试策略在每次单个超时时重试。为此,需要让重试策略处理超时策略抛出的TimeoutRejectedException异常。

    下面这个示例使用了上篇提到的Polly.Extensions.Http这个包,它可以很方便地为Http错误(比如HttpRequestException、Http 5XX和Http 408等)添加额外的处理。

    using Polly.Extensions.Http;
    
    var retryPolicy = HttpPolicyExtensions
        .HandleTransientHttpError()
        .Or<TimeoutRejectedException>() // 若超时则抛出此异常
        .WaitAndRetryAsync(new[]
            {
                TimeSpan.FromSeconds(1),
                TimeSpan.FromSeconds(5),
                TimeSpan.FromSeconds(10)
            });
    
    // 为每个重试定义超时策略
    var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(10);
    
    serviceCollection.AddHttpClient("GitHub", client =>
    {
        client.BaseAddress = new Uri("https://api.github.com/");
        client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
        client.Timeout = TimeSpan.FromSeconds(60); // 默认超时时间
    })
    .AddPolicyHandler(retryPolicy)
    // 将超时策略放在重试策略之内,每次重试会应用此超时策略
    .AddPolicyHandler(timeoutPolicy);
    

    用例:缓存策略

    Polly 的缓存策略可以在通过IHttpClientFactory配置的委托处理程序中使用。Polly是通用的(不与Http请求绑定),因此在编写代码时,Polly缓存策略从Polly.Context中确定要使用的缓存键。可以通过HttpRequestMessage请求上的一个扩展方法来设置这个参数:

    request.SetPolicyExecutionContext(new Polly.Context("CacheKeyToUseWithThisRequest"));
    

    由于Polly缓存策略是在HttpResponseMessage级别的委托代理服务上进行缓存,因此还需要考虑下面的问题。

    HttpResponseMessage级别的缓存是否合适?

    如果你想重用HttpResponseMessage,那么在HttpResponseMessage级别上进行缓存可能非常合适。

    但在某些情况下,比如调用WebService来获取一些序列化数据,然后反序列化到应用程序中的本地类型,HttpResponseMessage可能不是缓存的最佳粒度。

    在这些情况下,HttpResponseMessage级别上的缓存意味着每次命中缓存都会重复读取数据流和反序列化,这在性能方面是不必要的。

    在更高级别缓存可能更合适——例如,缓存流或反序列化到应用程序的本地类型的结果。

    缓存HttpResponseMessage还要考虑以下三点:

    • HttpResponseMessage可以包含HttpContent,它只能向前读取流(只能读取一次)。这可能意味着,当CachePolicy第二次从缓存中检索它时,除非重新初始化流指针,否则无法重新读取流。
    • 考虑去个性化和时间戳。缓存的个人特有信息和时间戳可能不适合重新提供给后续的请求。
    • 注意只缓存状态码为200(OK)的响应。考虑使用Response.EnsureSuccessStatusCode()等方法确保只有成功的响应才能传递给缓存策略。或者你可以使用这里(http://t.cn/Ehnr78P)描述的自定义ITtlStrategy。

    用例:在策略执行和调用之间交换信息

    Polly策略的每次执行都会携带Polly.Context类的一个执行域实例(execution-scoped instance),该类的作用是提供上下文,并允许在执行前、执行中和执行后阶段之间交换信息(译注:类似于HttpContext)。

    对于通过HttpClientFactory和Polly配置的HttpClient,可以在执行之前使用扩展方法HttpRequestMessage.SetPolicyExecutionContext(context)来设置被用于Http调用的上下文Polly.Context。该上下文具有字典语义,允许您传递任意数据。

    var context = new Polly.Context();
    context["MyCustomData"] = foo;
    
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUri);
    request.SetPolicyExecutionContext(context); 
    
    var response = await client.SendAsync(request, cancellationToken);
    

    Polly将该上下文实例作为输入参数传递给策略上配置的任何委托钩子(例如onRetry)。例如下面这个已经预先配置了策略的HttpClient:

    var retryPolicy = HttpPolicyExtensions
        .HandleTransientHttpError()
        .WaitAndRetryAsync(new[]
        {
            TimeSpan.FromSeconds(1),
            TimeSpan.FromSeconds(5),
            TimeSpan.FromSeconds(10)
        },
        onRetryAsync: async (outcome, timespan, retryCount, ctx) => {
            /* Do something with ctx["MyCustomData"] */
            // ...
        });
    

    委托钩子可以在执行期间设置其上下文信息:

    var retryPolicy = HttpPolicyExtensions
        .HandleTransientHttpError()
        .WaitAndRetryAsync(new[]
        {
            TimeSpan.FromSeconds(1),
            TimeSpan.FromSeconds(5),
            TimeSpan.FromSeconds(10)
        },
        onRetryAsync: async (outcome, timespan, retryCount, ctx) => {
            ctx["RetriesInvoked"] = retryCount;
            // ...
        });
    

    这些信息可以在执行后从上下文中读取:

    var response = await client.SendAsync(request, cancellationToken);
    
    var context = response.RequestMessage?.GetPolicyExecutionContext(); // 如果还没有保存在局部变量中
    if (context?.TryGetValue("RetriesInvoked", out int? retriesNeeded) ?? false)
    {
        // Do something with int? retriesNeeded
    }
    

    注意,只有在执行之前使用HttpRequestMessage.SetPolicyExecutionContext(context)设置了上下文时,HttpRequestMessage.GetPolicyExecutionContext()的获得的上下文才可用。

    相关阅读:

    .NET 开源项目 Polly 介绍

    在 .NET Core 中结合 HttpClientFactory 使用 Polly(上篇)

    在 .NET Core 中结合 HttpClientFactory 使用 Polly(中篇)

    作者:精致码农

    出处:http://cnblogs.com/willick

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如有问题或建议,请多多赐教,非常感谢。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值