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

public class HttpClientPool : IDisposable
{
    private readonly ConcurrentQueue _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();
        _semaphore = new SemaphoreSlim(maxPoolSize);
        _timeout = timeout;
        _maxRetries = maxRetries;
        _circuitBreakerTimeout = circuitBreakerTimeout;
        _consecutiveFailuresThreshold = consecutiveFailuresThreshold;
        _circuitBreakerTripped = false;
    }

    public async Task 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 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()
    {
        // 创建 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}");
        }
    }
}

你可能感兴趣的:(.net,服务器,运维)