.NET Core:使用 HttpClientFactory 和 Polly 构建坚如磐石的服务让我们从 IHttpClientFactory Polly 开始

2025-06-10

.NET Core:使用 HttpClientFactory 和 Polly 构建坚如磐石的服务

让我们从IHttpClientFactory

波莉

在这篇文章中,我将解释什么是 HttpClientFactory 和 Polly 重试策略以及为什么应该在下一个项目中使用它们。

让我们从IHttpClientFactory

很多客户打电话给我,希望我帮助他们解决突发和随机请求失败的问题。他们将一项新服务投入生产,最初几天运行良好,但突然之间,他们Application Insights中看到大量SocketException异常。

大多数情况下,这些失败是由于错误使用 HttpClient而导致套接字耗尽

HttpClient 的设计初衷是实例化一次,并在应用程序的整个生命周期内重复使用。如果每次请求都实例化一个HttpClient类,在高负载下会耗尽可用的套接字数量。

因此请避免使用如下代码:



using (var httpClient = new HttpClient())
{
    var result = await httpClient.GetAsync("https://www.bing.com");
}


Enter fullscreen mode Exit fullscreen mode

您可以在此处阅读有关此问题的更多详细信息:
https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

因此,为了缓解这个问题,.NET Core 团队在 .NET Core 2.1 中引入了IHttpClientFactory,它提供了以下好处:

  1. 提供命名和配置逻辑 HttpClient 对象的中心位置。例如,你可以配置一个预先配置为访问特定微服务的客户端(服务代理)。

  2. 通过在 HttpClient 中委托处理程序并实现基于 Polly 的中间件来编纂传出中间件的概念,以利用 Polly 的弹性策略。

  3. HttpClient 已经提供了委托处理程序的概念,这些处理程序可以链接在一起以处理传出的 HTTP 请求。您可以将 HTTP 客户端注册到工厂中,并使用 Polly 处理程序来使用 Polly 策略进行重试、断路器等操作。

  4. 管理 HttpMessageHandler 的生命周期以避免自己管理 HttpClient 生命周期时可能发生的上述问题。

让我们看一个利用 IHttpClientFactory 的简单示例。
在我的示例中,我将使用一个非常酷炫且免费的 API—— SuperHero (你可以在https://apilist.fun/找到更多免费 API 来试用)。

在新的 Web API 项目中,按照如下方式Startup.cs更改ConfigureServices方法:



public void ConfigureServices(IServiceCollection services)
{
    // the AddHttpClient() will provide us with an instance of HttpClient
    // available for Dependancy Injection in our services
    services.AddHttpClient<ISuperHeroService, SuperHeroService>(o =>                                    
                          o.BaseAddress = new Uri(Configuration["SuperHeroApiConfig:BaseUrl"]));

    services.AddControllers();
}


Enter fullscreen mode Exit fullscreen mode

现在让我们创建我们的SuperHeroService



public class SuperHeroService : ISuperHeroService
{

    private readonly HttpClient _httpClient;
    private readonly ISuperHeroApiConfig _superHeroApiConfig;

    public SuperHeroService(HttpClient httpClient, ISuperHeroApiConfig superHeroApiConfig)
    {
         _httpClient = httpClient;
         _superHeroApiConfig = superHeroApiConfig;
    }

     public async Task<PowerStats> GetPowerStats(int id)
     {
         return await _httpClient.GetFromJsonAsync<PowerStats>(
                $"{_superHeroApiConfig.AccessToken}/{id}/powerstats");
     }        
}


Enter fullscreen mode Exit fullscreen mode

最后但同样重要的是让我们创建我们的SuperHeroController



[ApiController]
[Route("api/[controller]")]
public class SuperHeroController : ControllerBase
{
    private readonly ILogger<SuperHeroController> _logger;
    private readonly ISuperHeroService _superHeroService;

    public SuperHeroController(ILogger<SuperHeroController> logger, ISuperHeroService superHeroService)
    {
        _logger = logger;
        _superHeroService = superHeroService;
    }

     [HttpGet("GetPowerStats/{id}")]
     public async Task<ActionResult> GetPowerStats(int id)
     {
         var result = await _superHeroService.GetPowerStats(id);

         return Ok(result);
     }       
}


Enter fullscreen mode Exit fullscreen mode

您可以使用 Postman 或任何其他应用程序来测试您的 Super Hero 服务:

Postman 示例

现在我们看到了使用 克服旧的 HttpClient 问题是多么容易IHttpClientFactory,让我们使用Polly让我们的 Super Hero 服务更能抵御故障。

波莉

Polly 是一个 .NET 弹性和瞬态故障处理库,允许开发人员以流畅且线程安全的方式表达重试、断路器、超时、舱壁隔离和回退等策略。

我想我们大多数人在某个时间点都看到过这样的代码,试图实现某种重试逻辑。



public async Task<PowerStats> GetPowerStats(int id)
{
      var currentRetry = 0;

       while (true)
       {
            try
            {
                return await _httpClient.GetFromJsonAsync<PowerStats>(
                                $"{_superHeroApiConfig.AccessToken}/{id}/powerstats");

             }
             catch (Exception ex)
             {
                currentRetry++;

                 if (currentRetry > 3)
                 {
                     throw;
                 }
            }
        }
}


Enter fullscreen mode Exit fullscreen mode

然而,我们都同意这种方法并不干净且易于维护,特别是在具有许多不同服务和端点的大型项目中。

Polly来救援!

步骤1:添加Polly nuget包Microsoft.Extensions.Http.Polly

第 2 步:ConfigureServices在方法中创建自定义策略Startup.cs



// Create the retry policy we want
var retryPolicy = HttpPolicyExtensions
                .HandleTransientHttpError() // HttpRequestException, 5XX and 408
                .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(retryAttempt));


Enter fullscreen mode Exit fullscreen mode

步骤 3:将其应用到我们的 SuperHeroService,非常简单!



services.AddHttpClient<ISuperHeroService, SuperHeroService>(o => 
                            o.BaseAddress = new Uri(Configuration["SuperHeroApiConfig:BaseUrl"]))
                .AddPolicyHandler(retryPolicy);


Enter fullscreen mode Exit fullscreen mode

那么当我们的 SuperHeroService 收到TransientHttpError(HttpRequestException, 5XX, 408) 时会发生什么?

我们的HttpClient会重试请求。
第一次重试会等待 1 秒后重试。
第二次重试会等待 2 秒后重试。
第三次也是最后一次重试会等待 3 秒后重试。
如果再次失败,则不会重试,直接返回。

这是一个非常简单的示例,仅用于演示基本Polly用法,但您可以做更多的事情。
您可以根据需要定制重试策略,只需几行代码即可应用断路器、超时、隔墙隔离和回退。

现在您已经掌握了构建坚如磐石的服务的知识,请使用它!

你可以在我的GitHub上找到完整的代码

这篇文章充满爱意❤️

鏂囩珷鏉ユ簮锛�https://dev.to/rickystam/net-core-use-httpclientfactory-and-polly-to-build-rock-solid-services-2edh
PREV
新的一年,新的 DEV 冒险!
NEXT
Angular:如何构建像 Outlook 一样的全屏日历,首先让我们拥有 Angular 组件,其次让我们看看 CalendarDay 类是什么样子,第三让我们用所需的日期填充日历,第四让我们添加一些 HTML 和 CSS 来实际开始显示日历,第五现在是时候解释和展示块管道的代码了