代码之家  ›  专栏  ›  技术社区  ›  Rob

如何使用dotnetcore和Polly添加动态重试策略

  •  1
  • Rob  · 技术社区  · 7 年前

    我有一个dotnetcore(2.1)控制台应用程序,我正在使用Polly用重试策略包装代码段。这在下面显示的一个简单用例中运行良好:

    private void ProcessRun() 
    {
        var policy = Policy.Handle<SocketException>().WaitAndRetryAsync(
                     retryCount: 3
                     sleepDurationProvider: attempt => TimeSpan.FromSeconds(10),
                     onRetry: (exception, calculatedWaitDuration) => 
                     {
                        Console.WriteLine($"Retry policy executed for type SocketException");
                     });
    
        try
        {
            CancellationTokenSource _cts = new CancellationTokenSource()
    
            PollyRetryWaitPolicy.ExecuteAsync(async token => {
               MyOperation(token);
            }, _cts.Token)
            .ContinueWith(p => {
               if (p.IsFaulted || p.Status == TaskStatus.Canceled)
               {
                    Console.WriteLine("faulted or was cancelled");
               }
            })
            .ConfigureAwait(false);
        }
        catch (Exception ex) {
         Console.WriteLine($"Exception has occurred: {ex.Message}");
        }
    }
    

    然后使用以下代码进行测试:

    private void MyOperation() 
    {
        Thread.Sleep(2000);
        throw new SocketException();
    }
    

    我在寻找一种灵活的方法,用多个策略而不是一个策略来包装代码。我修改了代码,动态地添加了许多打包的Polly重试策略,以允许捕获多个错误类型,并轻松地更改我正在查找的异常。我把代码改成:

    internal PolicyWrap PollyRetryWaitPolicy;
    
    public void AddRetryWaitPolicy<T>(int waitTimeInSeconds, int retryAttempts)
        where T: Exception
    {
    
        // Setup the polly policy that will be added to the executing code.
        var policy = Policy.Handle<T>().WaitAndRetryAsync(
                    retryCount: retryAttempts, 
                    sleepDurationProvider: attempt => TimeSpan.FromSeconds(waitTimeInSeconds), 
                    onRetry: (exception, calculatedWaitDuration) => 
                    {
                        Console.WriteLine($"Retry policy executed for type {typeof(T).Name}");
                    });
    
        if (host.PollyRetryWaitPolicy == null)
        {
            // NOTE: Only add this timeout policy as it seems to need at least one
            // policy before it can wrap! (suppose that makes sense).
            var timeoutPolicy = Policy
                .TimeoutAsync(TimeSpan.FromSeconds(waitTimeInSeconds), TimeoutStrategy.Pessimistic);
            PollyRetryWaitPolicy = policy.WrapAsync(timeoutPolicy);
        }
        else
        {
            PollyRetryWaitPolicy.WrapAsync(policy);
        }
    }
    
    private void ProcessRun() 
    {
        AddRetryWaitPolicy<SocketException>(10, 5);
        AddRetryWaitPolicy<InvalidOperationException>(5, 2);
    
        try
        {
            Console.WriteLine($"Calling HostedProcess.Run() method. AwaitResult: {awaitResult}");
    
            CancellationTokenSource _cts = new CancellationTokenSource()
    
            PollyRetryWaitPolicy.ExecuteAsync(async token => {
               MyOperation(token);
            }, _cts.Token)
            .ContinueWith(p => {
               if (p.IsFaulted || p.Status == TaskStatus.Canceled)
               {
                    Console.WriteLine("Process has faulted or was cancelled");
               }
            })
            .ConfigureAwait(false);
        }
        catch (Exception ex) {
         Console.WriteLine($"Exception has occurred: {ex.Message}");
        }
    }
    

    当我使用此代码进行测试时,上面的代码按预期工作,并重试5次。

    私有void MyOperation()
    睡眠(2000);
    抛出新的SocketException();
    

    但当我尝试以下操作时,它不会按预期重试2次(它根本不会重试):

    private void MyOperation() 
    {
        Thread.Sleep(2000);
        throw new InvalidOperationException();
    }
    

    提前谢谢你的指点!

    1 回复  |  直到 7 年前
        1
  •  1
  •   mountain traveller    7 年前

    在这里:

    if (host.PollyRetryWaitPolicy == null)
    {
        // NOTE: Only add this timeout policy as it seems to need at least one
        // policy before it can wrap! (suppose that makes sense).
        var timeoutPolicy = Policy
            .TimeoutAsync(TimeSpan.FromSeconds(waitTimeInSeconds), TimeoutStrategy.Pessimistic);
        PollyRetryWaitPolicy = policy.WrapAsync(timeoutPolicy);
    }
    else
    {
        PollyRetryWaitPolicy.WrapAsync(policy);
    }
    

    看来 if 分支机构和 else 分支采用不一致的方法对wrap中的重试策略和超时策略进行排序。

    • 如果
    • 其他的 分支,任何现有的重试和超时策略都将被包装 新的重试策略。所以超时策略是 外部 新的重试策略。
      • 新的重试策略设置为在之后重试 waitTimeInSeconds ; 但是超时策略也被设置为在执行之后超时 . 因此,一旦发生第一次重试(对于第二次或随后配置的重试策略),超时就会中止整个执行。所以重试从来没有发生过,正如你所观察到的。

    为了解决这个问题,你可以改变 其他的 分支机构:

    PollyRetryWaitPolicy = policy.WrapAsync(PollyRetryWaitPolicy);
    

    背景 :参见 PolicyWrap wiki recommendations on ordering policies in a PolicyWrap . 根据你的位置 TimeoutPolicy RetryPolicy , 时间策略 作为每次尝试的超时(在内部时),或作为所有尝试的总超时(在外部时)。