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();
}
}
调用端
文章来源地址https://www.toymoban.com/news/detail-741899.html
public class Program
{
public static async Task Main()
{
// 创建 HttpClientPool
var 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}");
}
}
}
文章来源:https://www.toymoban.com/news/detail-741899.html
到了这里,关于.net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!