Политика Polly для регистрации исключений и повторного выброса
Я полагаю, чтобы использовать Полли, чтобы создать политику, чтобы регистрировать исключения и перебрасывать. Я не нашел существующего метода, который бы позволял это из коробки, но некоторые варианты, которые я вижу, являются
Отступать
// Specify a substitute value or func, calling an action (eg for logging) if the fallback is invoked.
Policy.Handle<Whatever>()
.Fallback<UserAvatar>(UserAvatar.Blank, onFallback: (exception, context) =>
{ _logger.Log(exception, context);
throw exception;
});
Вопрос: нормально ли выкидывать исключение из Fallback?
Тайм - аут
Policy.Timeout(1, T30meoutStrategy.Pessimistic, (context, timespan, task) =>
{ task.ContinueWith(t =>
{ // ContinueWith important!: the abandoned task may very well still be executing, when the caller times out on waiting for it!
if (t.IsFaulted )
{
logger.Error(context,t.Exception);
throw exception;
} );
Или повторите
Policy.Handle<DivideByZeroException>().Retry(0, (exception, retryCount) =>
{ logger.Error(context,exception);
throw exception;
} );
Вопрос: поддерживается ли 0 попыток?
Или ПОЦЕЛУЙ и напиши простой try/catch с throw сам.
Какой из этих методов лучше? Каковы ваши рекомендации?
3 ответа
Если у вас еще нет Полли, попробуйте / поймать.
Если у вас уже есть Полли в смеси, FallbackPolicy
может быть безопасно переосмыслен так, как вы предлагаете. onFallback
действие делегата и резервное действие или значение не регулируются .Handle<>()
положения Политики, так что вы можете безопасно сбросить исключение из onFallback
делегировать.
Policy<UserAvatar>.Handle<Whatever>()
.Fallback<UserAvatar>(UserAvatar.Blank, onFallback: (exception, context) =>
{ _logger.Log(exception, context);
throw exception;
});
Подход вашего вопроса в общих чертах TimeoutPolicy
будет записывать только исключения, выданные делегатами, от которых ранее отказался вызывающий абонент из-за тайм-аута, и только в TimeoutMode.Pessimistic
; не все исключения.
Подход вашего вопроса в общих чертах .Retry(0, ...)
не будет работать. Если повторных попыток не указано, onRetry
делегат не будет вызван.
Чтобы избежать неопрятности перепрофилирования FallbackPolicy
Вы также можете написать свой собственный LogThenRethrowPolicy
в структурах Полли. Этот коммит (который добавил простое NoOpPolicy
) иллюстрирует минимум, необходимый для добавления новой политики. Вы можете добавить реализацию, аналогичную NoOpPolicy
но просто try { } catch { /* log; rethrow */ }
https://github.com/App-vNext/Polly-Samples/blob/master/PollyDemos/Async/AsyncDemo02_WaitAndRetryNTimes.cs показывает, что вы можете использовать onRetry:
вариант, по крайней мере для WaitAndRetryAsync. Я еще не смотрел на других.
HttpPolicyExtensions
.HandleTransientHttpError()
.WaitAndRetryAsync(3,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) // exponential back-off: 2, 4, 8 etc
+ TimeSpan.FromMilliseconds(Jitterer.Next(0, 1000)), // plus some jitter: up to 1 second
onRetry: (response, calculatedWaitDuration) =>
{
logger.LogError($"Failed attempt. Waited for {calculatedWaitDuration}. Retrying. {response.Exception.Message} - {response.Exception.StackTrace}");
}
);
Вот мое решение с общим методом
public async Task<T> PollyRetry<T>(
Func<Task<T>> action)
{
bool hasFallback = false;
Exception ex = null;
var fallbackPolicy = Policy<T>.Handle<Exception>().FallbackAsync(
default(T), d =>
{
//log final exception
ex = d.Exception;
hasFallback = true;
return Task.FromResult(new { });
});
var retryPolicy = Policy
.Handle<Exception>()
.WaitAndRetryAsync(3,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(res, timeSpan, context) =>
{
//log exception
});
var policyResult = await fallbackPolicy.WrapAsync(retryPolicy).ExecuteAndCaptureAsync(action);
if (hasFallback && ex != null)
throw ex;
return policyResult.Result;
}
//call service with retry logic
TestResponse response = await _pollyRetryService.PollyRetry(async () =>
{
return await _testService.Test(input);
});