.NET Core 中如何构建一个弹性的 HTTP 哀求机制?

打印 上一主题 下一主题

主题 987|帖子 987|积分 2961

1. 理解弹性 HTTP 哀求机制

什么是弹性?

弹性是指体系在面临故障或异常情况时,能够保持或快速恢复到正常状态的能力。在 HTTP 哀求的上下文中,弹性意味着当哀求失败时,体系能够主动采取一系列措施(如重试、降级、断路等)来确保哀求最终乐成或优雅地处置惩罚失败。
为什么需要弹性 HTTP 哀求机制?

在分布式体系中,服务间的依赖关系复杂,任何一个服务的故障都大概导致整个体系的不可用。弹性 HTTP 哀求机制可以帮助我们:

  • 提高体系的可用性:通过重试、断路等策略,减少因瞬态故障导致的体系不可用。
  • 加强用户体验:通过快速恢复和优雅降级,减少用户感知到的故障时间。
  • 降低运维成本:通过主动化处置惩罚故障,减少人工干预的需求。
弹性机制的核心原则


  • 重试(Retry):在哀求失败时,主动重试一定次数。
  • 断路器(Circuit Breaker):当失败率达到一定阈值时,暂时制止哀求,避免雪崩效应。
  • 超时(Timeout):设置哀求的超时时间,避免长时间等待。
  • 降级(Fallback):当哀求失败时,提供备用的响应或行为。
  • 负载均衡(Load Balancing):将哀求分散到多个服务实例,避免单点故障。
2. .NET Core 中的 HTTP 哀求根本

HttpClient 的使用

在 .NET Core 中,HttpClient 是用于发送 HTTP 哀求和接收 HTTP 响应的重要类。以下是一个简单的 HttpClient 使用示例:
  1. using System;
  2. using System.Net.Http;
  3. using System.Threading.Tasks;
  4. public class HttpClientApplication
  5. {
  6.     public static async Task Main(string[] args)
  7.     {
  8.         using (HttpClient client = new HttpClient())
  9.         {
  10.             // 发送 GET 请求
  11.             HttpResponseMessage response = await client.GetAsync("https://******");
  12.             if (response.IsSuccessStatusCode)
  13.             {
  14.                 // 读取响应内容
  15.                 string content = await response.Content.ReadAsStringAsync();
  16.                 Console.WriteLine(content);
  17.             }
  18.             else
  19.             {
  20.                 // 输出错误状态码
  21.                 Console.WriteLine($"Error: {response.StatusCode}");
  22.             }
  23.         }
  24.     }
  25. }
复制代码
HttpClientFactory 的引入

HttpClient 的直接使用存在一些问题,如 DNS 更新问题和套接字耗尽问题。为了解决这些问题,.NET Core 引入了 HttpClientFactory,它提供了更好的 HttpClient 生命周期管理和配置选项。
在 Startup.cs 中配置 HttpClientFactory:
  1. public class Startup
  2. {
  3.     public void ConfigureServices(IServiceCollection services)
  4.     {
  5.         // 注册 HttpClientFactory 并添加一个命名的 HttpClient
  6.         services.AddHttpClient("ResilientClient", client =>
  7.         {
  8.             client.BaseAddress = new Uri("https://******"); // 设置基础地址
  9.             client.DefaultRequestHeaders.Add("Accept", "application/json"); // 设置默认请求头
  10.         });
  11.     }
  12.     public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  13.     {
  14.         // 其他中间件配置
  15.     }
  16. }
复制代码
在控制器或服务中使用 HttpClientFactory:
  1. using Microsoft.AspNetCore.Mvc;
  2. using System.Net.Http;
  3. using System.Threading.Tasks;
  4. [ApiController]
  5. [Route("[controller]")]
  6. public class ResilientController : ControllerBase
  7. {
  8.     private readonly IHttpClientFactory _httpClientFactory;
  9.     public ResilientController(IHttpClientFactory httpClientFactory)
  10.     {
  11.         _httpClientFactory = httpClientFactory;
  12.     }
  13.     [HttpGet]
  14.     public async Task<IActionResult> Get()
  15.     {
  16.         // 通过名称获取 HttpClient 实例
  17.         var client = _httpClientFactory.CreateClient("ResilientClient");
  18.         // 发送 GET 请求
  19.         var response = await client.GetAsync("posts/list");
  20.         if (response.IsSuccessStatusCode)
  21.         {
  22.             var content = await response.Content.ReadAsStringAsync();
  23.             return Ok(content); // 返回成功响应
  24.         }
  25.         return StatusCode((int)response.StatusCode); // 返回错误状态码
  26.     }
  27. }
复制代码
优点:


  • 生命周期管理:HttpClientFactory 主动管理 HttpClient 的生命周期,避免套接字耗尽问题。
  • 配置机动:可以为差别的 API 配置差别的 HttpClient 实例。
  • DNS 更新支持:HttpClientFactory 会定期革新 DNS 缓存。
3. 实现根本的重试机制

简单的重试逻辑

在没有使用任何库的情况下,我们可以通过简单的循环来实现重试逻辑:
  1. public async Task<string> GetDataWithRetryAsync(int maxRetries = 3)
  2. {
  3.     int retryCount = 0;
  4.     while (true)
  5.     {
  6.         try
  7.         {
  8.             // 发送 GET 请求
  9.             HttpResponseMessage response = await _httpClient.GetAsync("data");
  10.             response.EnsureSuccessStatusCode(); // 确保请求成功
  11.             return await response.Content.ReadAsStringAsync(); // 返回响应内容
  12.         }
  13.         catch (HttpRequestException)
  14.         {
  15.             retryCount++;
  16.             if (retryCount >= maxRetries)
  17.             {
  18.                 throw; // 超过重试次数后抛出异常
  19.             }
  20.         }
  21.     }
  22. }
复制代码
使用 Polly 实现重试策略

Polly 是一个流行的 .NET 弹性库,提供了丰富的策略来实现重试、断路、超时等功能。以下是一个使用 Polly 实现重试策略的示例:
  1. using Polly;
  2. using Polly.Retry;
  3. public class RetryService
  4. {
  5.     private readonly HttpClient _httpClient;
  6.     private readonly AsyncRetryPolicy<HttpResponseMessage> _retryPolicy;
  7.     public RetryService(HttpClient httpClient)
  8.     {
  9.         _httpClient = httpClient;
  10.         // 配置重试策略:最多重试 3 次,每次等待 2 秒
  11.         _retryPolicy = Policy
  12.             .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode) // 处理失败响应
  13.             .Or<HttpRequestException>() // 处理请求异常
  14.             .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); // 指数退避
  15.     }
  16.     public async Task<string> GetDataWithRetryAsync()
  17.     {
  18.         // 执行重试策略
  19.         HttpResponseMessage response = await _retryPolicy.ExecuteAsync(() => _httpClient.GetAsync("data"));
  20.         response.EnsureSuccessStatusCode(); // 确保请求成功
  21.         return await response.Content.ReadAsStringAsync(); // 返回响应内容
  22.     }
  23. }
复制代码
重试策略的配置

Polly 允许我们机动地配置重试策略,包罗重试次数、重试间隔等。以下是一个配置指数退避重试策略的示例:
  1. _retryPolicy = Policy
  2.     .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
  3.     .Or<HttpRequestException>()
  4.     .WaitAndRetryAsync(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
复制代码
4. 处置惩罚瞬态故障

什么是瞬态故障?

瞬态故障是指那些暂时性的、通常会主动恢复的故障。比方,网络抖动、服务暂时不可用等。瞬态故障的特点是它们通常是短暂的,重试后大概会乐成。
常见的瞬态故障范例


  • 网络抖动:网络毗连不稳定导致的哀求失败。
  • 服务暂时不可用:目的服务因负载过高或维护而暂时不可用。
  • 资源限定:目的服务因资源限定(如 CPU、内存)而暂时无法处置惩罚哀求。
使用 Polly 处置惩罚瞬态故障

Polly 提供了多种策略来处置惩罚瞬态故障,包罗重试、断路、超时等。以下是一个结合重试和断路策略的示例:
  1.   // 定义重试策略,当HTTP请求失败时进行重试
  2.   var retryPolicy = Policy
  3.       .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
  4.       .Or<HttpRequestException>()
  5.       // 设置重试次数为3次,每次重试的间隔时间按指数递增(2^retryAttempt秒)
  6.       .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
  7.   // 定义熔断策略,当连续失败次数达到阈值时,熔断一段时间
  8.   var circuitBreakerPolicy = Policy
  9.       .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
  10.       .Or<HttpRequestException>()
  11.       .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); // 设置熔断条件:连续失败5次后,熔断30秒
  12.   // 将重试策略和熔断策略组合成一个综合策略
  13.   var combinedPolicy = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy);
  14.   HttpResponseMessage response = await combinedPolicy.ExecuteAsync(() => _httpClient.GetAsync("data"));
复制代码
5. 实现断路器模式

断路器模式的概念

断路器模式是一种用于防止体系因依赖服务故障而瓦解的设计模式。当依赖服务的失败率达到一定阈值时,断路器会打开,制止所有哀求,直到依赖服务恢复。
使用 Polly 实现熔断策略

Polly 提供了 CircuitBreaker 策略来实现熔断策略。以下是一个使用 Polly 实现熔断策略的示例:
  1. var circuitBreakerPolicy = Policy
  2.     .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
  3.     .Or<HttpRequestException>()
  4.     .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); // 连续失败 5 次后,断路器打开 30 秒
  5. HttpResponseMessage response = await circuitBreakerPolicy.ExecuteAsync(() => _httpClient.GetAsync("data"));
复制代码
配置熔断策略参数

Polly 允许我们配置熔断策略的参数,包罗失败次数阈值、断路时间等。以下是一个配置断路器的示例:
  1. var circuitBreakerPolicy = Policy
  2.     .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
  3.     .Or<HttpRequestException>()
  4.     .CircuitBreakerAsync(
  5.         exceptionsAllowedBeforeBreaking: 5, // 允许的失败次数
  6.         durationOfBreak: TimeSpan.FromSeconds(30) // 断路时间
  7.     );
复制代码
6. 超时和超时策略

设置哀求超时

在 HttpClient 中,我们可以通过 Timeout 属性设置哀求的超时时间:
  1. _httpClient.Timeout = TimeSpan.FromSeconds(10); // 设置超时时间为 10 秒
复制代码
使用 Polly 实现超时策略

Polly 提供了 Timeout 策略来实现超时控制。以下是一个使用 Polly 实现超时策略的示例:
  1. var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10)); // 设置超时时间为 10 秒
  2. HttpResponseMessage response = await timeoutPolicy.ExecuteAsync(() => _httpClient.GetAsync("data"));
复制代码
超时与重试的结合

我们可以将超时策略与重试策略结合使用,以应对因超时导致的哀求失败:
  1. var retryPolicy = Policy
  2.     .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
  3.     .Or<HttpRequestException>()
  4.     .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); // 重试策略
  5. var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10)); // 超时策略
  6. var combinedPolicy = Policy.WrapAsync(retryPolicy, timeoutPolicy); // 组合策略
  7. HttpResponseMessage response = await combinedPolicy.ExecuteAsync(() => _httpClient.GetAsync("data"));
复制代码
7. 负载均衡与哀求分流

负载均衡的根本概念

负载均衡是指将哀求分散到多个服务实例,以避免单点故障和提高体系的可扩展性。常见的负载均衡策略包罗轮询、随机、加权轮询等。
在 .NET Core 中实现负载均衡

在 .NET Core 中,我们可以通过配置多个 HttpClient 实例来实现负载均衡。以下是一个简单的负载均衡示例:
  1. public class LoadBalancer
  2. {
  3.     private readonly List<HttpClient> _httpClients;
  4.     private readonly Random _random = new Random();
  5.     public LoadBalancer(IHttpClientFactory httpClientFactory)
  6.     {
  7.         _httpClients = new List<HttpClient>
  8.         {
  9.             httpClientFactory.CreateClient("ServiceInstance1"), // 实例 1
  10.             httpClientFactory.CreateClient("ServiceInstance2"), // 实例 2
  11.             httpClientFactory.CreateClient("ServiceInstance3")  // 实例 3
  12.         };
  13.     }
  14.     public async Task<string> GetDataAsync()
  15.     {
  16.         // 随机选择一个 HttpClient 实例
  17.         HttpClient client = _httpClients[_random.Next(_httpClients.Count)];
  18.         HttpResponseMessage response = await client.GetAsync("data");
  19.         response.EnsureSuccessStatusCode();
  20.         return await response.Content.ReadAsStringAsync();
  21.     }
  22. }
复制代码
哀求分流的策略

哀求分流是指根据某些条件(如哀求内容、用户身份等)将哀求分发到差别的服务实例。以下是一个简单的哀求分流示例:
  1. public async Task<string> GetDataAsync(string userId)
  2. {
  3.     // 根据用户 ID 选择不同的 HttpClient 实例
  4.     HttpClient client = userId.StartsWith("A") ? _httpClients[0] : _httpClients[1];
  5.     HttpResponseMessage response = await client.GetAsync("data");
  6.     response.EnsureSuccessStatusCode();
  7.     return await response.Content.ReadAsStringAsync();
  8. }
复制代码
8. 监控与日志记载

监控 HTTP 哀求的重要性

监控 HTTP 哀求可以帮助我们实时发现和解决问题,确保体系的稳定性和可靠性。常见的监控指标包罗哀求乐成率、响应时间、错误率等。
使用 Application Insights 进行监控

Application Insights 是 Azure 提供的一个应用性能管理服务,可以帮助我们监控和分析 HTTP 哀求。以下是一个使用 Application Insights 监控 HTTP 哀求的示例:
  1. public class HttpRemoteService
  2. {
  3.     private readonly HttpClient _httpClient;
  4.     private readonly TelemetryClient _telemetryClient;
  5.     public HttpRemoteService(HttpClient httpClient, TelemetryClient telemetryClient)
  6.     {
  7.         _httpClient = httpClient;
  8.         _telemetryClient = telemetryClient;
  9.     }
  10.     public async Task<string> GetDataAsync()
  11.     {
  12.         var startTime = DateTime.UtcNow;
  13.         var timer = System.Diagnostics.Stopwatch.StartNew();
  14.         try
  15.         {
  16.             HttpResponseMessage response = await _httpClient.GetAsync("data");
  17.             response.EnsureSuccessStatusCode();
  18.             return await response.Content.ReadAsStringAsync();
  19.         }
  20.         catch (Exception ex)
  21.         {
  22.             _telemetryClient.TrackException(ex); // 记录异常
  23.             throw;
  24.         }
  25.         finally
  26.         {
  27.             timer.Stop();
  28.             _telemetryClient.TrackDependency("HTTP", "GET", "data", startTime, timer.Elapsed, true); // 记录依赖调用
  29.         }
  30.     }
  31. }
复制代码
日志记载的最佳实践

日志记载是监控和调试的重要工具。以下是一些日志记载的最佳实践:

  • 记载关键信息:如哀求 URL、响应状态码、响应时间等。
  • 使用结构化日志:便于日志的查询和分析。
  • 避免记载敏感信息:如密码、令牌等。
  1. public async Task<string> GetDataAsync()
  2. {
  3.     _logger.LogInformation("正在发送 HTTP GET 请求到 {Url}", "https://api.*****.com/data");
  4.     try
  5.     {
  6.         HttpResponseMessage response = await _httpClient.GetAsync("data");
  7.         response.EnsureSuccessStatusCode();
  8.         string content = await response.Content.ReadAsStringAsync();
  9.         _logger.LogInformation("请求成功,响应状态码: {StatusCode}", response.StatusCode);
  10.         return content;
  11.     }
  12.     catch (Exception ex)
  13.     {
  14.         _logger.LogError(ex, "请求失败: {Message}", ex.Message);
  15.         throw;
  16.     }
  17. }
复制代码
参考资料

结语

在 .NET Core 中构建一个弹性的 HTTP 哀求机制是一个复杂但值得的使命。盼望本文能够帮助你在 .NET Core 中构建一个结实的 HTTP 哀求机制。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

干翻全岛蛙蛙

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表