C# - 通用重试机制






4.74/5 (13投票s)
通用重试机制的演示
引言
在开发应用程序时,你可能需要按顺序执行一组方法来完成业务需求。如果其中一个方法发生异常,整个过程将会失败。异常可能发生在连接外部服务、网络连接问题或其他外部因素时。如果你以指定的时间间隔重试失败的过程,你可能会得到一个成功的结果,并且可以执行其余的方法。
你可以在两种不同的情况下使用重试机制:
- 执行方法时发生异常
- 方法可能没有返回预期的结果
应用程序演示
我想解释一个演示应用程序来描述重试机制的需求。
需要从main
方法执行三个方法,如果失败,main
方法将重试失败的方法。
/// <summary>
/// Send mail
/// </summary>
/// <returns>true/false</returns>
private static bool SendEmail()
{
Console.WriteLine("Sending Mail ...");
// simulate error
Random rnd = new Random();
var rndNumber = rnd.Next(1, 10);
if (rndNumber != 3)
throw new SmtpFailedRecipientException();
Console.WriteLine("Mail Sent successfully");
return true;
}
/// <summary>
/// Create database connection
/// </summary>
/// <returns>true/false</returns>
private static bool ConnectDatabase()
{
Console.WriteLine("Trying to connect database ...");
// simulate the error
throw new Exception("Not able to connect database");
}
/// <summary>
/// Random number
/// </summary>
/// <returns>random number</returns>
private static int GetRandomNumber()
{
Random rnd = new Random();
// Generate the random number between 1-10
var result = rnd.Next(1, 10);
Console.WriteLine(result.ToString());
return result;
}
static void Main(string[] args)
{
// Send mail with 10 retries
Console.WriteLine("Send mail, if it fails then try 10 times");
var isSuccess = Retry.Execute(() => SendEmail(), new TimeSpan(0, 0, 1), 10, true);
if (!isSuccess)
Console.WriteLine("Unable to send a mail");
// Connect database
var connectionStatus = Retry.Execute(() => ConnectDatabase(), new TimeSpan(0, 0, 2), 5, true);
if (!connectionStatus)
Console.WriteLine("Unable connect database");
// Print Random number till the random number is 4
Console.WriteLine("Print Random number till the random number is 4");
Retry.Execute(() => GetRandomNumber(), new TimeSpan(0, 0, 1), 10, 4);
Console.ReadLine();
}
以下是在不同情况下实现重试机制的方法。
输出
通用重试类
public class Retry
{
/// <summary>
/// Generic Retry
/// </summary>
/// <typeparam name="TResult">return type</typeparam>
/// <param name="action">Method needs to be executed</param>
/// <param name="retryInterval">Retry interval</param>
/// <param name="retryCount">Retry Count</param>
/// <param name="expectedResult">Expected Result</param>
/// <param name="isExpectedResultEqual">true/false to check equal
/// or not equal return value</param>
/// <param name="isSuppressException">
/// Suppress exception is true / false</param>
/// <returns></returns>
public static TResult Execute<TResult>(
Func<TResult> action,
TimeSpan retryInterval,
int retryCount,
TResult expectedResult,
bool isExpectedResultEqual = true,
bool isSuppressException = true
)
{
TResult result = default(TResult);
bool succeeded = false;
var exceptions = new List<Exception>();
for (int retry = 0; retry < retryCount; retry++)
{
try
{
if (retry > 0)
Thread.Sleep(retryInterval);
// Execute method
result = action();
if (isExpectedResultEqual)
succeeded = result.Equals(expectedResult);
else
succeeded = !result.Equals(expectedResult);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
if (succeeded)
return result;
}
if (!isSuppressException)
throw new AggregateException(exceptions);
else
return result;
}
}