指定放弃/失败动作
本文关键字:失败 放弃 | 更新日期: 2023-09-27 18:15:55
我正在使用Polly发出HTTP请求,如果请求失败,则重试5次。
当5次尝试失败并且策略放弃时,是否可以指定一个操作?
在下面的代码中;当我们失败了5次,我知道用户没有网络,所以我想显示一个消息框,说"应用程序需要网络"。我可以使用计数器来计数5次失败,但使用Polly方法会更好。
var policy = Polly.Policy.Handle<Exception>().WaitAndRetryAsync(
5,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(ex, span) =>
{
Mvx.Trace("Retried because of {0}", ex);
}
);
await policy.ExecuteAsync(() => MakeRequestEx<T>(requestUrl, verb, accept, headers, baseAddress)).ConfigureAwait(false);
是的,您可以使用ExecuteAndCapture
var policyResult = await policy.ExecuteAndCaptureAsync(
() => MakeRequestEx<T>(requestUrl, verb, accept, headers, baseAddress)
).ConfigureAwait(false);
检查policyResult
的Outcome
是否呼叫失败,并显示提示信息。
你的方法有几个问题,让我试着描述一下
不是所有的运算都是幂等的
基于您的共享代码,我假设您的MakeRequestEx
函数是通用的,足以发出GET
或POST
动词的请求。如果你盲目地对任何类型的请求应用重试逻辑,那么你可能会得到不想要的重复或不可逆的副作用。
这就是为什么为了使用重试逻辑,您应该满足以下条件组:
- 潜在引入的可观察影响是可接受的
- 手术可重做,无不可逆副作用
- 与承诺的可靠性相比,引入的复杂性可以忽略不计
大多数情况下,GET
的操作是以幂等方式写的,但其他(CRU)操作则不是这样。如果您想使用您的策略+ MakeRequestEx
,请确保您的下游系统支持所有动词的请求重复数据删除。
不是所有的错误都是短暂的
如果抛出异常,您希望根据共享代码重试。这种方法的问题是,并不是所有的异常都代表一个暂时的问题。
如果你重新执行一个操作,比如10/0
,那么无论你想重试多少次,你总是会收到一个DivideByZeroException
。http请求也是如此。例如,如果url无效,那么您将收到UrlFormatException
。如果你重新做N次,它仍然是一个UrlFormatException
,因为它不是一个短暂的失败。
大多数情况下,如果您收到HttpRequestException
或响应状态码是这些:408,429或5XX之一,则重新执行http请求就足够了。
计数重试次数
当我们失败了5次时,我知道用户没有互联网,所以我想显示一个消息框,说'应用程序需要互联网'
重试策略的Execute
和ExecuteAsync
方法的工作方式是,如果在N次重试后操作不能成功完成,则抛出最后一次尝试的异常。
所以,如果你用try
- catch
块包装你的ExecuteAsync
,那么你可以确保当执行流到catch
块时,所有的重试都失败了。
try
{
await policy.ExecuteAsync(() => MakeRequestEx<T(...));
}
catch
{
MessageBox.Show("Operation failed after 6 attempts");
}
在MessageBox
的消息中,我已经写了6次尝试,因为您已经将策略配置为5次重试,但是还有第0次尝试。
如果你不想使用try
- catch
块,那么你可以使用ExecuteAndCapture
/ExecuteAndCaptureAsync
方法。这些方法不会抛出异常,如果所有重试尝试失败,而是填充PolicyResult
的FinalException
属性,并将Outcome
属性设置为Failure
。
var retryResult = await policy.ExecuteAndCaptureAsync(() => MakeRequestEx<T(...));
if (retryResult.Outcome == Outcome.Failure)
MessageBox.Show("Operation failed after 6 attempts");
如果您想知道发出了多少重试尝试,那么您需要使用Context
对象。ExecuteAsync
/ExecuteAndCaptureAsync
是在AsyncPolicy
基类上定义的,所以它们不是特定于重试策略的,这就是为什么它们不公开发出的重试尝试。
如果你像这样定义一些辅助方法:
public static class ContextExtensions
{
private static readonly string key = "RetryCount";
public static void IncreaseRetryCount(this Context context)
{
var retryCount = GetRetryCount(context);
context[key] = ++retryCount;
}
public static int GetRetryCount(this Context context)
{
context.TryGetValue(key, out object count);
return count != null ? (int)count : 0;
}
}
则可以调用
- 每次重试时的
IncreaseRetryCount
- 策略执行后的
GetRetryCount
var policy = Polly.Policy
.Handle<Exception>()
.WaitAndRetryAsync(5,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(ex, _, ctx) =>
{
Mvx.Trace("Retried because of {0}", ex);
ctx.IncreaseRetryCount();
});
var result = await policy.ExecuteAndCaptureAsync(() => MakeRequestEx<T(...));
var outcome = result.Outcome == OutcomeType.Successful ? "completed" : "failed";
MessageBox.Show($"Operation has {outcome} after the initial attempt + {result.Context.GetRetryCount()} retry attempts");