您当前的位置: 首页 > 

寒冰屋

暂无认证

  • 0浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

面向对象的重试模式方法

寒冰屋 发布时间:2021-02-09 20:34:13 ,浏览量:0

目录

背景

重试模式

实现

使用代码

兴趣点

  • 下载源代码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

关注
打赏
1665926880
查看更多评论
立即登录/注册

微信扫码登录

0.0922s