1 polly
I\xe2\x80\x98m 使用 polly 对 HTTP POST 请求进行简单的重试 n 次 szenario。它应该处理任何异常并重试将我的负载发送到 api 端点 n 次。因此,我使用了 WaitAndRetryPolicy 来包装 TimoutPolicy,并采用悲观策略来实现每次尝试超时。两者都是异步策略。
\n\n当重试情况发生时,所做的每次重试尝试都会在重新建立连接后发布到端点。
\n\n封装这两个策略的方法:
\n\n\n public static PolicyWrap WaitAndRetryNTimesWithTimeoutPerTry(int n, TimeSpan sleepDuration, TimeSpan retryTimeout)\n {\n var waitAndRetryPolicy = Policy.Handle<Exception>().WaitAndRetryAsync(\n retryCount: n,\n sleepDurationProvider: attempt => sleepDuration,\n onRetry: (exception, waitDuration, ctx) =>\n {\n Debug.WriteLine($"[Polly.OnRetry due \'{exception.Message}\'; waiting for {waitDuration.TotalMilliseconds} ms before retrying.");\n }\n );\n\n var timeoutPerTryPolicy = Policy.TimeoutAsync(\n retryTimeout, TimeoutStrategy.Pessimistic);\n\n return waitAndRetryPolicy.WrapAsync(timeoutPerTryPolicy);\n }\nRun Code Online (Sandbox Code Playgroud)\n\n调用Web api的代码:
\n\n\n var waitAndRetry5TimesWithShortTimeout = ResiliencePolicyFactory.WaitAndRetryNTimesWithTimeoutPerTry(\n n: 5,\n sleepDuration: TimeSpan.FromMilliseconds(700),\n retryTimeout: TimeSpan.FromMilliseconds(2300));\n }\n\n try\n {\n await waitAndRetry5TimesWithShortTimeout.ExecuteAndCaptureAsync(async token =>\n {\n if (!cancellationToken.IsCancellationRequested)\n {\n response = await client.PostAsync(uri, content, cancellationToken);\n if (response.IsSuccessStatusCode)\n {\n Debug.WriteLine($"[{nameof(CheckinService)}] ===>> Now Checked in!");\n }\n }\n }, cancellationToken);\n }\n catch(Exception ex)\n {\n throw new ApplicationException("NoCheckInPossible", ex);\n }\n\nRun Code Online (Sandbox Code Playgroud)\n\n当代码遇到重试情况并在多次重试后成功时,每次重试尝试都会发布到端点,即使 I\xe2\x80\x99m 将取消令牌传递给 ExecuteAsync-Task 和 HttpClient。
\n\n根据我的理解,第一个成功的请求应该取消所有待处理的重试。有人能指出,我\xe2\x80\x99m 做错了什么吗?
\n问题看起来是这一行:
response = await client.PostAsync(uri, content, cancellationToken);
Run Code Online (Sandbox Code Playgroud)
正在使用名为 的变量,而不是Polly 传递给在 处执行的委托的cancellationToken变量。tokenasync token =>
使用以下内容应该可以修复它:
response = await client.PostAsync(uri, content, token);
Run Code Online (Sandbox Code Playgroud)
Polly 超时策略将超时组合CancellationToken到调用者传递到执行中的任何取消令牌中,但要使该超时令牌产生任何效果,在执行的委托中,您必须使用 Polly 提供给执行的令牌(token在本例中为变量) )。
(从问题中发布的代码中,我们看不到任何信号取消cancellationToken;如果有,请评论或编辑问题以澄清。)
使用代码client.PostAsync(uri, content, cancellationToken),如果没有任何取消cancellationToken,则每个 POST 永远不会被取消,这可能解释了为什么您会看到多个 POST 运行完成。
我制作了一个与您发布的代码接近的可运行的可重现示例,以进行演示。
public static Random rand = new Random();
public static async Task Main()
{
var waitAndRetry5TimesWithShortTimeout = WaitAndRetryNTimesWithTimeoutPerTry(
n: 5,
sleepDuration: TimeSpan.FromMilliseconds(70),
retryTimeout: TimeSpan.FromMilliseconds(230));
CancellationToken cancellationToken = new CancellationTokenSource().Token;
string response;
try
{
await waitAndRetry5TimesWithShortTimeout.ExecuteAndCaptureAsync(async token =>
{
Console.WriteLine("Placing call");
if (!cancellationToken.IsCancellationRequested)
{
response = await PretendPostAsync(cancellationToken); // Change 'cancellationToken' to 'token' here, and it will start to work as expected.
if (response == "success")
{
Console.WriteLine($"Now Checked in!");
}
}
}, cancellationToken);
}
catch(Exception ex)
{
throw new ApplicationException("NoCheckInPossible", ex);
}
}
public static async Task<string> PretendPostAsync(CancellationToken token)
{
if (rand.Next(4) != 0)
{
await Task.Delay(TimeSpan.FromSeconds(0.5), token);
}
return "success";
}
public static AsyncPolicyWrap WaitAndRetryNTimesWithTimeoutPerTry(int n, TimeSpan sleepDuration, TimeSpan retryTimeout)
{
var waitAndRetryPolicy = Policy.Handle<Exception>().WaitAndRetryAsync(
retryCount: n,
sleepDurationProvider: attempt => sleepDuration,
onRetry: (exception, waitDuration, ctx) =>
{
Console.WriteLine($"[Polly.OnRetry due '{exception.Message}'; waiting for {waitDuration.TotalMilliseconds} ms before retrying.");
}
);
var timeoutPerTryPolicy = Policy.TimeoutAsync(
retryTimeout, TimeoutStrategy.Pessimistic);
return waitAndRetryPolicy.WrapAsync(timeoutPerTryPolicy);
}
Run Code Online (Sandbox Code Playgroud)
您可以在此处的 DotNetFiddle 中运行它,并看到它通常给出如下输出:
Placing call
[Polly.OnRetry due 'The delegate executed asynchronously through TimeoutPolicy did not complete within the timeout.'; waiting for 70 ms before retrying.
Placing call
Now Checked in!
[Polly.OnRetry due 'The delegate executed asynchronously through TimeoutPolicy did not complete within the timeout.'; waiting for 70 ms before retrying.
Placing call
Now Checked in!
Run Code Online (Sandbox Code Playgroud)
(代码示例随机化以模拟不同程度的故障;您可能需要运行几次才能看到类似的结果。)
显然会放置多个调用 ( Placing call),并且多次运行直至完成 ( Now Checked in!),因为没有任何东西可以取消它们。
将指示的线路改为use token,可以看到,即使多次调用,之前的尝试也会被取消,只有一次成功。
Placing call
[Polly.OnRetry due 'The delegate executed asynchronously through TimeoutPolicy did not complete within the timeout.'; waiting for 70 ms before retrying.
Placing call
[Polly.OnRetry due 'The delegate executed asynchronously through TimeoutPolicy did not complete within the timeout.'; waiting for 70 ms before retrying.
Placing call
Now Checked in!
Run Code Online (Sandbox Code Playgroud)
因为HttpClient.PostAsync(...)确实 Honor CancellationTokens,所以您可以使用稍微更高效的TimeoutStrategy.Optimistic.
| 归档时间: |
|
| 查看次数: |
5815 次 |
| 最近记录: |