重试策略





4.00/5 (2投票s)
一个通用的重试策略实用程序,用于 .NET Core。
引言
“如果一开始不成功,那就重试 X 次,每次尝试之间有可配置的延迟”,没人这样说过。
工程师可以遵循所有最佳实践,但错误仍然会发生。问题可能完全超出开发人员的控制,例如网络或硬盘故障。有些系统具有弹性,几秒钟内,故障转移就会接管,系统健康状况恢复正常。不幸的是,错误已经报告给用户,他们需要采取进一步的行动。用户只是再次点击相同的按钮而不做任何更改的情况并不少见。“神奇地”,“它有效了”,用户留下了一个不好的印象。对 App 的负面评价随之而来,声称它不可靠或“愚蠢”。 在这种情况下,RetryPolicy
(重试策略)非常有用,它可以自动重新提交相同的操作,以防发生短暂的故障。 如果成功,最终用户不会知道发生了错误并且得到了优雅的处理。
背景
重试一个动作并期望得到不同的结果在现实世界中非常常见。汽车里的人会转动点火开关,汽车无法启动,然后再次尝试,直到得到想要的结果。当无法更改电视上的频道或无法从自动售货机中取出零食时,人们会再次尝试相同的动作,直到电视响应或零食被送达。这种行为在视频游戏、移动应用程序和网站中也很常见。作为软件工程师,我们可以使用RetryPolicy
自动化这个过程,并为用户提供更好的体验。
RetryPolicy
可能并非在每种情况下都有效。第一步是确定何时何地可能需要重试操作。 一种可能的用途是当网络延迟可能导致问题时,或者在提交到数据库时,临时表锁可能会导致短期故障。 一旦确定了好的用途,可能无法执行的代码将作为委托传递给RetryPolicy
。
Using the Code
RetryPolicy
类需要一个日志记录器工厂、重试限制和重试延迟。 这些被private
方法Execute
和ExecuteAsync
使用。 这两个函数中的逻辑相同,其中一个设计用于异步代码。 它将执行代码,如果发生故障,它将尝试再次执行该代码,直到成功或达到最大重试限制。 流程图如下所示
项目中的控制台应用程序演示了用法。 RetryPolicy
类实例化后,传递给ExecuteAsync
和Execute
方法的代码将由RetryPolicy
运行,直到成功或达到退出条件。 当达到执行计数限制或执行条件委托返回成功时,RetryPolicy
将退出。 下面的示例代码随机抛出一个Exception
来模拟一个将被优雅处理的错误。
private static async Task Main(string[] args)
{
ILoggerFactory loggerFactory = new LoggerFactory();
loggerFactory.AddConsole(LogLevel.Debug);
var logger = loggerFactory.CreateLogger(typeof(Program));
var random = new Random();
var retryPolicy = new RetryPolicy(loggerFactory, 7, 500);
var executionAttemptCount = 0;
//Execute async Example
await retryPolicy.ExecuteAsync(async (token) =>
{
var randomNumber = random.Next(1, 3);
logger.LogInformation("Executing Async");
if (randomNumber % 2 == 1)
{
logger.LogInformation("Simulating Random Transient Exception");
throw new Exception("Random Transient Exception");
}
logger.LogInformation("Execution Async Complete");
await Task.Yield();
});
//Execute async Example with delegate condition
executionAttemptCount = 0;
await retryPolicy.ExecuteAsync(async (token) =>
{
var randomNumber = random.Next(1, 3);
executionAttemptCount++;
logger.LogInformation("Executing Async with custom exit condition");
if (randomNumber % 2 == 1)
{
logger.LogInformation("Simulating Random Transient Exception");
throw new Exception("Random Transient Exception");
}
logger.LogInformation("Execution Async Complete with custom exit condition");
await Task.Yield();
}, async (token) =>
{
if (executionAttemptCount % 2 == 1)
{
return await Task.FromResult(false);
}
else
{
return await Task.FromResult(true);
}
});
//Execute sync example
retryPolicy.Execute(() =>
{
var randomNumber = random.Next(1, 3);
logger.LogInformation("Executing");
if (randomNumber % 2 == 1)
{
logger.LogInformation("Simulating Random Transient Exception");
throw new Exception("Random Transient Exception");
}
logger.LogInformation("Execution Complete");
});
//Execute sync Example with delegate condition
executionAttemptCount = 0;
retryPolicy.Execute(() =>
{
var randomNumber = random.Next(1, 3);
executionAttemptCount++;
logger.LogInformation("Executing with custom exit condition");
if (randomNumber % 2 == 1)
{
logger.LogInformation("Simulating Random Transient Exception");
throw new Exception("Random Transient Exception");
}
logger.LogInformation("Execution Complete with custom exit condition");
}, () =>
{
if (executionAttemptCount % 2 == 1)
{
return false;
}
else
{
return true;
}
});
System.Console.ReadLine();
}
下面的控制台输出精确地描述了在发生错误时使用RetryPolicy
会发生什么
- 执行委托
- 发生随机故障(例如,网络断开、硬盘错误、表锁)
- 在 500 毫秒的延迟后,再次尝试
- 这次没有随机故障
- 执行成功完成
关注点
此项目面向 dotnetcore 2.x。
Github 上的源代码: https://github.com/SenseiCris/RetryPolicy
历史
- 版本 1.0 – 初始版本