目录
背景
重试模式
实现
使用代码
兴趣点
- 下载源代码11 KB
最近,当我在代码中修复某些内容时,偶然发现了重试的实现,该实现是有效的,但效果不是很好。它与该类的实际逻辑交织在一起,使单元测试不必要地变得复杂。实际上,我们的公共库中有一些不错的实现,所以我采用并重用了它。
重试模式我们的应用程序在其中运行的环境本质上是不可靠的。尤其是当您与应用程序外部的资源(例如服务或存储)进行通信时。这些错误可以是永久性的(找不到资源,或者您无权使用该资源),也可以是暂时性的(超时或您的调用受到限制)。如果可以确定哪个故障是暂时的,则可以自动重试这些操作,而不必将故障返回给调用方。
成功的重试策略必须满足以下要求:
- 确定故障是永久的还是暂时的。
- 如果出现瞬态故障,将自动重试该操作。重试的次数必须限制为不会无限循环,以防万一瞬态故障毕竟不是那么瞬态。
- 在重试之间插入某种延迟,以使资源有一定的恢复空间。
如果您想了解有关重试逻辑的更多信息,请参阅Microsoft Docs上的以下文章:
- 重试一般指南——云应用程序的最佳做法
- 重试模式——云设计模式
让我们从重试策略本身开始。我在这里选择async版本,因为我认为这在当今很普遍。可以很容易地将其重写为不使用任务。而且,将要重试的动作是返回数据。同样,您可能有一个单独的重载而不返回数据,但我的意思并不是要提供完整的实现只是为了展示面向对象的设计。
public interface IRetryStrategy
{
Task Execute(Func action);
}
我们将基于异常处理错误。
public interface IExceptionHandler
{
bool IsTransient(Exception ex);
}
最后,我们还有另一个依赖关系来确保重试之间的延迟。
public interface IDelayProvider
{
TimeSpan GetNextValue();
}
这是重试策略的简化实现。请查看随附的完整版本代码。在这里,您还可以找到异常处理程序和延迟提供程序的实现。
public async Task Execute(Func action)
{
var retryCounter = 0;
while (true)
{
try
{
return await action();
}
catch (Exception ex)
{
if (retryCounter >= this.maxRetry || !this.exceptionHandler.IsTransient(ex))
{
throw;
}
}
retryCounter++;
var delay = this.delayProvider.GetNextValue();
await Task.Delay(delay);
}
}
使用代码
重试策略具有多个依赖关系,我们使用工厂来创建它。通常,您希望它是可配置的(重试次数,延迟次数等),这是使用工厂的另一个参数。这是一个示例,它看起来可能是什么样子,但是您需要根据自己的需要进行定制。
public class RetryStrategyFactory : IRetryStrategyFactory
{
public IRetryStrategy Create()
{
var exceptionHandler = new HttpRequestExceptionHandler();
var delayProvider = new ExponentialBackoffDelayProvider(TimeSpan.FromSeconds(0.6),
TimeSpan.FromSeconds(6), 2.0);
return new RetryStrategy(5, exceptionHandler, delayProvider, new WaitWrapper());
}
}
然后,您每次需要执行不可靠的操作时,都可以使用工厂创建重试策略。请注意,您可能希望针对不同的操作采用不同的策略。
public async Task Process()
{
var retryStrategy = this.retryStrategyFactory.Create();
var data = await retryStrategy.Execute(() =>
{
// retrieve data
});
// process data
}
兴趣点
此实现几乎与我们在生产代码中使用的实现相同。您可以争辩说,在面向对象设计方面还有改进的余地,我同意。例如,与接口相比,使用委托非常方便且极为容易,但是如在测试中,进行模拟有点棘手,这表明设计并不理想。我当时正在考虑进一步推动设计,但如果有足够的兴趣,也许以后再考虑。
https://www.codeproject.com/Tips/5292290/Object-oriented-Approach-to-Retry-Pattern