using System.Threading.Tasks; using System.Threading; using AIK.Models.HttpPolicy; using AIK.Service.IService; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace AIK.Service.Service { public class RetryPolicyService : IRetryPolicyService { private readonly ILogger _logger; private readonly RetryPolicyConfig _config; public RetryPolicyService(ILogger logger, RetryPolicyConfig config = null) { _logger = logger; _config = config ?? new RetryPolicyConfig(); } public async Task ExecuteWithRetryAsync(Func> operation, string operationName, CancellationToken cancellationToken = default) { var exceptions = new List(); for (int retryCount = 0; retryCount < _config.MaxRetryCount; retryCount++) { cancellationToken.ThrowIfCancellationRequested(); try { return await operation(); } catch (Exception ex) when (IsTransientException(ex) && retryCount < _config.MaxRetryCount - 1) { exceptions.Add(ex); _logger.LogWarning(ex, "操作 {OperationName} 第 {RetryCount} 次失败,{RetryDelay}ms 后重试", operationName, retryCount + 1, _config.RetryDelay.TotalMilliseconds); await Task.Delay(_config.RetryDelay, cancellationToken); } catch (Exception ex) when (IsTransientException(ex)) { exceptions.Add(ex); _logger.LogError(ex, "操作 {OperationName} 最终失败,已重试 {MaxRetryCount} 次", operationName, _config.MaxRetryCount); throw new AggregateException($"操作 {operationName} 失败,已重试 {_config.MaxRetryCount} 次", exceptions); } } throw new InvalidOperationException("不应该执行到这里,未知的情况"); } public async Task ExecuteWithRetryAsync(Func operation, string operationName, CancellationToken cancellationToken = default) { await ExecuteWithRetryAsync(async () => { await operation(); return null; }, operationName, cancellationToken); } private bool IsTransientException(Exception ex) { return ex is TimeoutException || ex is TaskCanceledException || ex is HttpRequestException || (ex is OperationCanceledException && !(ex is TaskCanceledException)) || IsNetworkRelatedException(ex); } private bool IsNetworkRelatedException(Exception ex) { var message = ex.Message.ToLower(); return message.Contains("connection") || message.Contains("network") || message.Contains("timeout") || message.Contains("socket") || message.Contains("dns"); } } }