当前位置: 首页 > news >正文

.net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池

public class HttpClientPool : IDisposable
{private readonly ConcurrentQueue<HttpClient> _httpClientPool; // HttpClient 对象池private readonly SemaphoreSlim _semaphore; // 控制同时访问 HttpClient 对象池的线程数private readonly TimeSpan _timeout; // 获取 HttpClient 的超时时间private readonly int _maxRetries; // 最大重试次数private readonly TimeSpan _circuitBreakerTimeout; // 熔断器超时时间private readonly int _consecutiveFailuresThreshold; // 连续失败阈值private bool _circuitBreakerTripped; // 熔断器是否触发private DateTime _circuitBreakerTrippedTime; // 熔断器触发时间private int _consecutiveFailures; // 连续失败计数器public HttpClientPool(int maxPoolSize, // 最大 HttpClient 对象池大小TimeSpan timeout, // 获取 HttpClient 的超时时间int maxRetries, // 最大重试次数TimeSpan circuitBreakerTimeout, // 熔断器超时时间int consecutiveFailuresThreshold // 连续失败阈值){_httpClientPool = new ConcurrentQueue<HttpClient>();_semaphore = new SemaphoreSlim(maxPoolSize);_timeout = timeout;_maxRetries = maxRetries;_circuitBreakerTimeout = circuitBreakerTimeout;_consecutiveFailuresThreshold = consecutiveFailuresThreshold;_circuitBreakerTripped = false;}public async Task<HttpResponseMessage> SendRequestWithRetries(string url){var retryCount = 0;_consecutiveFailures = 0;while (retryCount <= _maxRetries){try{var httpClient = await GetHttpClientAsync();var response = await httpClient.GetAsync(url);if (response.IsSuccessStatusCode){_consecutiveFailures = 0; // 重置连续失败计数器return response;}else{_consecutiveFailures++;if (_consecutiveFailures >= _consecutiveFailuresThreshold){TripCircuitBreaker(); // 连续失败达到阈值,触发熔断器throw new InvalidOperationException("连续失败次数达到阈值,熔断器已触发。");}retryCount++;}}catch (Exception ex){_consecutiveFailures++;if (_consecutiveFailures >= _consecutiveFailuresThreshold){TripCircuitBreaker(); // 连续失败达到阈值,触发熔断器throw new InvalidOperationException("连续失败次数达到阈值,熔断器已触发。");}retryCount++;}}throw new Exception($"重试 {_maxRetries} 次后仍然无法发送请求。");}private async Task<HttpClient> GetHttpClientAsync(){if (_circuitBreakerTripped){var elapsedTime = DateTime.Now - _circuitBreakerTrippedTime;if (elapsedTime < _circuitBreakerTimeout){throw new InvalidOperationException("熔断器已触发,请稍后重试。");}else{// 重置熔断器_circuitBreakerTripped = false;}}if (await _semaphore.WaitAsync(_timeout)){if (_httpClientPool.TryDequeue(out var httpClient)){return httpClient;}}throw new TimeoutException("获取 HttpClient 超时。");}public void ReturnHttpClient(HttpClient httpClient, bool success){if (success){_httpClientPool.Enqueue(httpClient);_semaphore.Release();}else{// 触发熔断器TripCircuitBreaker();httpClient.Dispose();_semaphore.Release();}}private void TripCircuitBreaker(){_circuitBreakerTripped = true;_circuitBreakerTrippedTime = DateTime.Now;_consecutiveFailures = 0;}public void Dispose(){foreach (var httpClient in _httpClientPool){httpClient.Dispose();}_httpClientPool.Clear();_semaphore.Dispose();}
}

调用端
 

public class Program
{public static async Task Main(){// 创建 HttpClientPoolvar httpClientPool = new HttpClientPool(maxPoolSize: 10,timeout: TimeSpan.FromSeconds(5),maxRetries: 3,circuitBreakerTimeout: TimeSpan.FromMinutes(10),consecutiveFailuresThreshold: 5);try{var response = await httpClientPool.SendRequestWithRetries("https://api.example.com");var content = await response.Content.ReadAsStringAsync();Console.WriteLine(content);}catch (Exception ex){Console.WriteLine($"请求失败:{ex.Message}");}}
}

相关文章:

  • 【Linux】第六站:Centos系统如何安装软件?
  • GCC 编译器 详细总结
  • 刷题笔记day08-字符串01
  • 编译支持GPU的opencv,并供python的import cv2调用
  • 程序员想要网上接单却看花了眼?那这几个平台你可得收藏好了!
  • C++之初始化列表详细剖析
  • 【Algorithm】最容易理解的蒙特卡洛树搜索(Monte Carlo Tree Search,MCTS)算法
  • 华纳云:centos系统中怎么查看cpu信息?
  • 【智能座舱系列】- 深度解密小米Hyper OS,华为HarmonyOS区别
  • 【从0到1设计一个网关】过滤器链的实现---实现负载均衡过滤器
  • postgresql|数据库|SQL语句冲突的解决
  • 2023年CCF中国开源大会“大模型时代的智能化软件工程新范式”分论坛成功举行...
  • zookeeper安装配置
  • 基于nodejs+vue客户管理管理系统
  • 天拓四方分享:企业安全生产管控系统的构建、实施与优化
  • 【译】理解JavaScript:new 关键字
  • Android交互
  • mysql中InnoDB引擎中页的概念
  • REST架构的思考
  • SAP云平台里Global Account和Sub Account的关系
  • vue-loader 源码解析系列之 selector
  • vue数据传递--我有特殊的实现技巧
  • web标准化(下)
  • 从零开始在ubuntu上搭建node开发环境
  • 将 Measurements 和 Units 应用到物理学
  • 今年的LC3大会没了?
  • 前端临床手札——文件上传
  • 深入 Nginx 之配置篇
  • 手写双向链表LinkedList的几个常用功能
  • 我的业余项目总结
  • 智能网联汽车信息安全
  • 从如何停掉 Promise 链说起
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • #### go map 底层结构 ####
  • #Lua:Lua调用C++生成的DLL库
  • #大学#套接字
  • #我与Java虚拟机的故事#连载06:收获颇多的经典之作
  • (173)FPGA约束:单周期时序分析或默认时序分析
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (zt)最盛行的警世狂言(爆笑)
  • (三)mysql_MYSQL(三)
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .net core开源商城系统源码,支持可视化布局小程序
  • .NET/C# 中设置当发生某个特定异常时进入断点(不借助 Visual Studio 的纯代码实现)
  • .net2005怎么读string形的xml,不是xml文件。
  • .NET中 MVC 工厂模式浅析
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • .vue文件怎么使用_vue调试工具vue-devtools的安装
  • :“Failed to access IIS metabase”解决方法
  • @Autowired @Resource @Qualifier的区别
  • [ C++ ] STL priority_queue(优先级队列)使用及其底层模拟实现,容器适配器,deque(双端队列)原理了解
  • [1204 寻找子串位置] 解题报告
  • [20180224]expdp query 写法问题.txt