面向对象的重试模式方法

目录

背景

重试模式

实现

使用代码

兴趣点


背景

最近,当我在代码中修复某些内容时,偶然发现了重试的实现,该实现是有效的,但效果不是很好。它与该类的实际逻辑交织在一起,使单元测试不必要地变得复杂。实际上,我们的公共库中有一些不错的实现,所以我采用并重用了它。

重试模式

我们的应用程序在其中运行的环境本质上是不可靠的。尤其是当您与应用程序外部的资源(例如服务或存储)进行通信时。这些错误可以是永久性的(找不到资源,或者您无权使用该资源),也可以是暂时性的(超时或您的调用受到限制)。如果可以确定哪个故障是暂时的,则可以自动重试这些操作,而不必将故障返回给调用方。

成功的重试策略必须满足以下要求:

  • 确定故障是永久的还是暂时的。
  • 如果出现瞬态故障,将自动重试该操作。重试的次数必须限制为不会无限循环,以防万一瞬态故障毕竟不是那么瞬态。
  • 在重试之间插入某种延迟,以使资源有一定的恢复空间。

如果您想了解有关重试逻辑的更多信息,请参阅Microsoft Docs上的以下文章:

实现

让我们从重试策略本身开始。我在这里选择async版本,因为我认为这在当今很普遍。可以很容易地将其重写为不使用任务。而且,将要重试的动作是返回数据。同样,您可能有一个单独的重载而不返回数据,但我的意思并不是要提供完整的实现只是为了展示面向对象的设计。

public interface IRetryStrategy
{
    Task<T> Execute<T>(Func<Task<T>> action);
}

我们将基于异常处理错误。

public interface IExceptionHandler
{
    bool IsTransient(Exception ex);
}

最后,我们还有另一个依赖关系来确保重试之间的延迟。

public interface IDelayProvider
{
    TimeSpan GetNextValue();
}

这是重试策略的简化实现。请查看随附的完整版本代码。在这里,您还可以找到异常处理程序和延迟提供程序的实现。

public async Task<T> Execute<T>(Func<Task<T>> 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值