在 C# 中使用 Polly,我可以等到时间跨度过去或任务终止后再重试吗?
Using Polly in C#, can I wait until a timespan elapses OR a task terminates before retrying?
我正在尝试在 C# 中使用 Polly 包。我想 运行 一些代码,然后,如果失败,请等待并重试。目前我的循环看起来类似于:
var successful = false
while (!successful){
// Try to perform operation.
successful = TryToDoStuff()
if (!successful){
// Wait, then retry.
await Task.WhenAny(
taskCompletionSource1.Task,
taskCompletionSource1.Task,
Task.Delay(TimeSpan.FromSeconds(10)));
}
}
即:等待 10 秒或直到这些任务完成源之一收到信号并终止。然后重试。
我想做的是这样的(Polly不支持API):
Policy
.Handle<RetryException>()
.WaitAndRetryForever(
Task.WhenAny(
taskCompletionSource1.Task,
taskCompletionSource1.Task,
Task.Delay(TimeSpan.FromSeconds(10))))
.Execute(TryToDoStuff); // Method TryToDoStuff will throw RetryException if it fails
是否可以用 Polly 做这样的事情?我可以等待 TimeSpan 以外的任何东西吗?
关于我在上面的例子中等待的两个任务:一个任务是一个取消,表示整个事情应该关闭。另一个是“唤醒连接尝试”任务,其终止指示“此对象的状态已更改;尝试再次调用它”。在这两种情况下,我都希望我的循环立即继续下一次迭代,而不是等待超时结束。
目前等待超时还不错,因为它只有 10 秒,但如果我将其更改为指数退避,那么突然超时可能会很长。因此,我希望中断超时并直接进行下一次迭代。
注意:不是我的重试循环必须遵循异步等待模式。如果等待部分是同步和阻塞的就可以了。我只想能够使用任务完成源取消等待。
我能想到的最佳解决方案是:
var successful = false
while (!successful){
// Create cancellation token that gets cancelled when one of the tasks terminates.
var cts = new CancellationTokenSource();
_ = Task.Run(async () =>
{
await Task.WhenAny(
taskCompletionSource1.Task,
taskCompletionSource1.Task);
cts.Cancel();
});
// Try to perform operation.
Policy
.Handle<RetryException>()
.WaitAndRetryForever(
TimeSpan.FromSeconds(10))
.Execute(
// Method TryToDoStuff will throw RetryException if it fails
ct => TryToDoStuff(),
// Pass in cancellation token.
cts.Token);
}
这似乎有效。但如果没有 Polly,我可能最终会这样做。
所有 Polly 策略和执行都可以 respond to CancellationToken
s to signal cancellation。
如果我没理解错的话,有两个要求:
- 如果发出取消信号,立即重试(不再延迟)
- 如果
RetryException
发生 ,则延迟重试(10 秒或指数)
您可以用 Polly 策略来表达:
var retryImmediatelyOnCancellation = Policy
.Handle<OperationCanceledException>()
.RetryForever();
var retryWithDelay = Policy
.Handle<RetryException>()
.WaitAndRetryForever(/* specify your desired delay or delay sequence for retries */);
然后以嵌套方式执行两个重试策略。
类似于:
retryImmediatelyOnCancellation.Execute(() =>
{
CancellationTokenSource externalCancellation = ... // Get the CancellationTokenSource signalling external cancellation.
CancellationTokenSource wakeUpConnectionAttemptCancellation = ... // Get the CancellationTokenSource signalling "wake up connection attempt".
CancellationTokenSource combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(externalCancellation.Token, wakeUpConnectionAttemptCancellation.Token);
retryWithDelay.Execute(ct => TryDoStuff(), combinedCancellationSource.Token);
});
如果发出 CancellationToken
信号,retryWithDelay
政策造成的延迟会立即取消。
CancellationToken
是比 TaskCompletionSource
更自然的抵消信号。但是,如果您需要(无论出于何种原因)坚持使用 TaskCompletionSource
作为这些事件的信号,您可以将其转换为取消 CancellationTokenSource
,使用一些简单的方法,例如:
taskCompletionSource.Task.ContinueWith(t => cancellationTokenSource.Cancel());
请注意,TaskCompletionSource
和 CancellationToken
都是一次性的:一旦完成或取消,它们将无法重置(未完成或未取消)。上面的代码示例将 CancellationTokenSource
s 的创建移动到 retry-on-cancellation 循环中,以便您在每个取消信号后获得新鲜的 CancellationTokenSource
s。
如果您切换到异步,所有这些也适用于异步 Polly 策略。
我正在尝试在 C# 中使用 Polly 包。我想 运行 一些代码,然后,如果失败,请等待并重试。目前我的循环看起来类似于:
var successful = false
while (!successful){
// Try to perform operation.
successful = TryToDoStuff()
if (!successful){
// Wait, then retry.
await Task.WhenAny(
taskCompletionSource1.Task,
taskCompletionSource1.Task,
Task.Delay(TimeSpan.FromSeconds(10)));
}
}
即:等待 10 秒或直到这些任务完成源之一收到信号并终止。然后重试。
我想做的是这样的(Polly不支持API):
Policy
.Handle<RetryException>()
.WaitAndRetryForever(
Task.WhenAny(
taskCompletionSource1.Task,
taskCompletionSource1.Task,
Task.Delay(TimeSpan.FromSeconds(10))))
.Execute(TryToDoStuff); // Method TryToDoStuff will throw RetryException if it fails
是否可以用 Polly 做这样的事情?我可以等待 TimeSpan 以外的任何东西吗?
关于我在上面的例子中等待的两个任务:一个任务是一个取消,表示整个事情应该关闭。另一个是“唤醒连接尝试”任务,其终止指示“此对象的状态已更改;尝试再次调用它”。在这两种情况下,我都希望我的循环立即继续下一次迭代,而不是等待超时结束。
目前等待超时还不错,因为它只有 10 秒,但如果我将其更改为指数退避,那么突然超时可能会很长。因此,我希望中断超时并直接进行下一次迭代。
注意:不是我的重试循环必须遵循异步等待模式。如果等待部分是同步和阻塞的就可以了。我只想能够使用任务完成源取消等待。
我能想到的最佳解决方案是:
var successful = false
while (!successful){
// Create cancellation token that gets cancelled when one of the tasks terminates.
var cts = new CancellationTokenSource();
_ = Task.Run(async () =>
{
await Task.WhenAny(
taskCompletionSource1.Task,
taskCompletionSource1.Task);
cts.Cancel();
});
// Try to perform operation.
Policy
.Handle<RetryException>()
.WaitAndRetryForever(
TimeSpan.FromSeconds(10))
.Execute(
// Method TryToDoStuff will throw RetryException if it fails
ct => TryToDoStuff(),
// Pass in cancellation token.
cts.Token);
}
这似乎有效。但如果没有 Polly,我可能最终会这样做。
所有 Polly 策略和执行都可以 respond to CancellationToken
s to signal cancellation。
如果我没理解错的话,有两个要求:
- 如果发出取消信号,立即重试(不再延迟)
- 如果
RetryException
发生 ,则延迟重试(10 秒或指数)
您可以用 Polly 策略来表达:
var retryImmediatelyOnCancellation = Policy
.Handle<OperationCanceledException>()
.RetryForever();
var retryWithDelay = Policy
.Handle<RetryException>()
.WaitAndRetryForever(/* specify your desired delay or delay sequence for retries */);
然后以嵌套方式执行两个重试策略。
类似于:
retryImmediatelyOnCancellation.Execute(() =>
{
CancellationTokenSource externalCancellation = ... // Get the CancellationTokenSource signalling external cancellation.
CancellationTokenSource wakeUpConnectionAttemptCancellation = ... // Get the CancellationTokenSource signalling "wake up connection attempt".
CancellationTokenSource combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(externalCancellation.Token, wakeUpConnectionAttemptCancellation.Token);
retryWithDelay.Execute(ct => TryDoStuff(), combinedCancellationSource.Token);
});
如果发出 CancellationToken
信号,retryWithDelay
政策造成的延迟会立即取消。
CancellationToken
是比 TaskCompletionSource
更自然的抵消信号。但是,如果您需要(无论出于何种原因)坚持使用 TaskCompletionSource
作为这些事件的信号,您可以将其转换为取消 CancellationTokenSource
,使用一些简单的方法,例如:
taskCompletionSource.Task.ContinueWith(t => cancellationTokenSource.Cancel());
请注意,TaskCompletionSource
和 CancellationToken
都是一次性的:一旦完成或取消,它们将无法重置(未完成或未取消)。上面的代码示例将 CancellationTokenSource
s 的创建移动到 retry-on-cancellation 循环中,以便您在每个取消信号后获得新鲜的 CancellationTokenSource
s。
如果您切换到异步,所有这些也适用于异步 Polly 策略。