兼容win7版应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

89 lines
3.3 KiB

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<RetryPolicyService> _logger;
private readonly RetryPolicyConfig _config;
public RetryPolicyService(ILogger<RetryPolicyService> logger, RetryPolicyConfig config = null)
{
_logger = logger;
_config = config ?? new RetryPolicyConfig();
}
public async Task<T> ExecuteWithRetryAsync<T>(Func<Task<T>> operation, string operationName, CancellationToken cancellationToken = default)
{
var exceptions = new List<Exception>();
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<Task> operation, string operationName, CancellationToken cancellationToken = default)
{
await ExecuteWithRetryAsync<object>(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");
}
}
}