using System;
using System.Collections.Concurrent;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;/// <summary>
/// 表示一个支持重试、熔断和超时策略的 HttpClient 实例池。
/// </summary>
public class HttpClientPool : IDisposable
{private ConcurrentQueue<HttpClientWrapper> httpClientPool;private SemaphoreSlim semaphore;private TimeSpan timeout;private int maxRetryAttempts;private TimeSpan retryInterval;private int circuitBreakerThreshold;private TimeSpan circuitBreakerDuration;private DateTime circuitBreakerOpenedUntil;/// <summary>/// 使用指定的池大小、超时时间、重试策略和熔断策略初始化 HttpClientPool 类的新实例。/// </summary>/// <param name="maxPoolSize">池的最大大小。</param>/// <param name="timeout">从池中获取 HttpClient 实例的超时时间。</param>/// <param name="maxRetryAttempts">最大重试次数。</param>/// <param name="retryInterval">重试间隔。</param>/// <param name="circuitBreakerThreshold">触发熔断的连续失败次数。</param>/// <param name="circuitBreakerDuration">熔断持续时间。</param>public HttpClientPool(int maxPoolSize, TimeSpan timeout, int maxRetryAttempts, TimeSpan retryInterval, int circuitBreakerThreshold, TimeSpan circuitBreakerDuration){httpClientPool = new ConcurrentQueue<HttpClientWrapper>();semaphore = new SemaphoreSlim(maxPoolSize);this.timeout = timeout;this.maxRetryAttempts = maxRetryAttempts;this.retryInterval = retryInterval;this.circuitBreakerThreshold = circuitBreakerThreshold;this.circuitBreakerDuration = circuitBreakerDuration;circuitBreakerOpenedUntil = DateTime.MinValue;}/// <summary>/// 异步从池中获取一个 HttpClient 实例。/// </summary>/// <returns>表示异步操作的任务。任务结果包含获取的 HttpClient 实例。</returns>public async Task<HttpClient> GetHttpClientAsync(){HttpClientWrapper wrapper = null;if (semaphore.Wait(timeout)){if (httpClientPool.TryDequeue(out wrapper)){return wrapper.HttpClient;}}return await CreateNewHttpClientAsync();}/// <summary>/// 释放 HttpClientPool 实例及其关联的所有资源,包括池中的 HttpClient 实例。/// </summary>public void Dispose(){while (httpClientPool.TryDequeue(out var wrapper)){wrapper.Dispose();}}private async Task<HttpClient> CreateNewHttpClientAsync(){var httpClient = new HttpClient();var retryAttempts = 0;while (retryAttempts <= maxRetryAttempts){if (DateTime.UtcNow >= circuitBreakerOpenedUntil){try{return await Task.FromResult(httpClient);}catch (HttpRequestException){retryAttempts++;if (retryAttempts <= maxRetryAttempts){await Task.Delay(retryInterval);}}}else{await Task.Delay(circuitBreakerDuration);}}throw new Exception("超过最大重试次数。");}private class HttpClientWrapper : IDisposable{public HttpClient HttpClient { get; }public HttpClientWrapper(HttpClient httpClient){HttpClient = httpClient;}public void Dispose(){ReleaseHttpClient(HttpClient);}}private void ReleaseHttpClient(HttpClient httpClient){var wrapper = new HttpClientWrapper(httpClient);httpClientPool.Enqueue(wrapper);semaphore.Release();}
}
调用端
using (var httpClientPool = new HttpClientPool(10, TimeSpan.FromSeconds(5), 3, TimeSpan.FromSeconds(2), 2, TimeSpan.FromSeconds(30)))
{using (var httpClient = await httpClientPool.GetHttpClientAsync()){// 使用 HttpClient 发送请求var response = await httpClient.GetAsync("https://example.com");// 处理响应// ...}
}