Как добавить динамические политики повторов, используя ядро ​​dotnet и Polly - PullRequest
0 голосов
/ 01 октября 2018

У меня есть консольное приложение dotnet core (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 Retry вместе, чтобы можно было перехватывать более одного типа ошибок и легко изменять исключения, которые я ищу.Я изменил код на следующий:

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 раз.

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

НО, когда я пытаюсь сдалее он не повторяется 2 раза, как ожидалось (он не повторяется вообще):

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

Что я делаю не так?Смысл всего вышеперечисленного заключается в динамическом множествении политик, как мне требуется.Есть ли лучший способ сделать что-то кроме WrapPolicy?

Спасибо за указатели заранее!

1 Ответ

0 голосов
/ 02 октября 2018

Здесь:

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 применяют непоследовательный подход к последовательности политики повтора и политики тайм-аута в переносе.

  • В ветви if политика тайм-аута включена в политику повторов.Таким образом, тайм-аут действует как тайм-аут на попытку.
  • В ветви else любая существующая политика повторов и тайм-аутов переносится за пределы новой политики повторных попыток.Таким образом, политика тайм-аута вне новой политики повторных попыток.
    • Новая политика повторных попыток настроена на повтор после 0 waitTimeInSeconds;но политика тайм-аута также настроена на тайм-аут выполнения после waitTimeInSeconds.Поэтому, как только происходит первая повторная попытка (для настроенной второй или последующей политики повторных попыток), тайм-аут прерывает все выполнение.Так что повторение никогда не происходит, как вы заметили.

Чтобы вылечить это, вы можете изменить ветку else на:

PollyRetryWaitPolicy = policy.WrapAsync(PollyRetryWaitPolicy);

Справочная информация : См. Рекомендации PolicyWrap wiki по упорядочению политик в PolicyWrap .В зависимости от того, поместите ли вы TimeoutPolicy внутри или снаружи RetryPolicy, TimeoutPolicy действует (когда внутри) как тайм-аут на попытку, или (когда снаружи) - как общее время ожидания для всех попыток.

...