使用 Polly 与 Task.TimeoutAfter 相反

发布于 2025-01-15 19:05:38 字数 1743 浏览 2 评论 0原文

如何使用 Polly 来代替 TimeoutAfter

我想在下面的代码中使用 Polly,而不是 TimeoutAfter

public async Task StartAsync()
{
    await _webSocket.ConnectAsync(_uri, CancellationToken.None).TimeoutAfter(OpenTimeoutMs).ConfigureAwait(false);

    ...

    await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None).TimeoutAfter(CloseTimeoutMs);
}

public static async Task TimeoutAfter(this Task task, TimeSpan timeout)
{
    try
    {
        await task.WaitAsync(timeout).ConfigureAwait(false);
    }
    catch (TimeoutException ex)
    {
        throw new TimeoutException($"Task timed out after {timeout}");
    }
}

我的想法(不确定我是否正确)

为了让 Polly 处理事情,我需要传递一个 CancellationToken > 到 StartAsync 并将 CancellationToken.None 替换为它。

这是我的想法,不知道对不对。这就是我问这个问题的原因。

var timeoutPolicy = Policy
    .Handle<TimeoutException>()
    .TimeoutAsync(TimeSpan.FromMilliseconds(timeoutMs), TimeoutStrategy.Optimistic,
        (context, timeSpan, task, ex) =>
        {
            Console.WriteLine($"Task timed out after {timeSpan.TotalSeconds} seconds");
            return Task.CompletedTask;
        });

await retryPolicy.ExecuteAsync(async (ct) =>
{
    await client.StartAsync(ct);
}, CancellationToken.None);

// changed the method to accept CancellationToken
public async Task StartAsync(CancellationToken ct = default)
{
    await _webSocket.ConnectAsync(_uri, ct).ConfigureAwait(false);

    ...

    await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct).ConfigureAwait(false);
}

How do I use Polly in oppose to TimeoutAfter?

I want to use Polly in the following code instead of TimeoutAfter:

public async Task StartAsync()
{
    await _webSocket.ConnectAsync(_uri, CancellationToken.None).TimeoutAfter(OpenTimeoutMs).ConfigureAwait(false);

    ...

    await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None).TimeoutAfter(CloseTimeoutMs);
}

public static async Task TimeoutAfter(this Task task, TimeSpan timeout)
{
    try
    {
        await task.WaitAsync(timeout).ConfigureAwait(false);
    }
    catch (TimeoutException ex)
    {
        throw new TimeoutException(
quot;Task timed out after {timeout}");
    }
}

My thoughts (not sure if I'm correct)

In order Polly to handle things, I need to pass a CancellationToken to StartAsync and replace CancellationToken.None with it.

This is what I think, I don't know if it's correct or not. That's why I ask the question.

var timeoutPolicy = Policy
    .Handle<TimeoutException>()
    .TimeoutAsync(TimeSpan.FromMilliseconds(timeoutMs), TimeoutStrategy.Optimistic,
        (context, timeSpan, task, ex) =>
        {
            Console.WriteLine(
quot;Task timed out after {timeSpan.TotalSeconds} seconds");
            return Task.CompletedTask;
        });

await retryPolicy.ExecuteAsync(async (ct) =>
{
    await client.StartAsync(ct);
}, CancellationToken.None);

// changed the method to accept CancellationToken
public async Task StartAsync(CancellationToken ct = default)
{
    await _webSocket.ConnectAsync(_uri, ct).ConfigureAwait(false);

    ...

    await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct).ConfigureAwait(false);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

野生奥特曼 2025-01-22 19:05:38

您的 TimeoutAfter 扩展方法会应用两次,分别针对 ConnectAsyncCloseAsync 方法。这意味着每个方法调用都有一个“本地”超时。

在您的 Polly 版本中,您定义了“全局”/“总体”超时,其中包括从 ConnectAsyncCloseAsync 的所有内容。

因此,这两个版本不相同相同。

顺便说一句,您的代码有一些问题:

  • TimeoutAsync 不能有 Handle 子句
  • retryPolicy 在此范围内未定义

您可以替换您的 TimeoutAfter 代码以利用 Polly:

public static async Task TimeoutAfter(Func<CancellationToken, Task> task, CancellationToken token, TimeSpan timeout)
{
    try
    {
        await CreateTimeoutConstraint(timeout)
            .ExecuteAsync(async (ct) => await task(ct), token)
            .ConfigureAwait(false);
    }
    catch (TimeoutRejectedException ex)
    {
        throw new OperationCanceledException($"Task timed out after {timeout}", ex);
    }
}

private static IAsyncPolicy CreateTimeoutConstraint(TimeSpan threshold)
    => Policy.TimeoutAsync(threshold, TimeoutStrategy.Optimistic,
    (context, timeSpan, task, ex) =>
    {
        Console.WriteLine($"Task timed out after {timeSpan.TotalSeconds} seconds");
        return Task.CompletedTask;
    });
  • 为了尊重用户取消令牌和超时取消令牌,您必须更改签名
    • 该方法应接收一个预期 CancellationToken 并返回 Task 的函数
    • 它还应该接收用户取消令牌
  • ExecuteAsync 调用中,您“结合" TimeoutPolicy 和用户取消令牌
    • 如果令牌首先请求取消,则ExecuteAsync将抛出TaskCanceledException
    • 如果 TimeoutPolicy 首先请求取消,则 ExecuteAsync 将抛出 TimeoutRejectedException,该异常会转换为 OperationCanceledException

该方法的用法如下所示:

await TimeoutAfter((token) => _webSocket.ConnectAsync(_uri, token), ct, OpenTimeoutMs);

...

await TimeoutAfter((token) => _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", token), ct, CloseTimeoutMs);

Your TimeoutAfter extension method is applied twice, separately for the ConnectAsync and for the CloseAsync methods. It means that each method call has a "local" timeout.

In your Polly version you have defined a "global" / "overarching" timeout which includes everything from ConnectAsync till CloseAsync.

So, the two versions are not identical.

BTW your code have some problems:

  • TimeoutAsync can't have a Handle clause
  • retryPolicy is not defined in this scope

You can replace your TimeoutAfter code to this to utilize Polly:

public static async Task TimeoutAfter(Func<CancellationToken, Task> task, CancellationToken token, TimeSpan timeout)
{
    try
    {
        await CreateTimeoutConstraint(timeout)
            .ExecuteAsync(async (ct) => await task(ct), token)
            .ConfigureAwait(false);
    }
    catch (TimeoutRejectedException ex)
    {
        throw new OperationCanceledException(
quot;Task timed out after {timeout}", ex);
    }
}

private static IAsyncPolicy CreateTimeoutConstraint(TimeSpan threshold)
    => Policy.TimeoutAsync(threshold, TimeoutStrategy.Optimistic,
    (context, timeSpan, task, ex) =>
    {
        Console.WriteLine(
quot;Task timed out after {timeSpan.TotalSeconds} seconds");
        return Task.CompletedTask;
    });
  • In order to respect both the user cancellation token and timeout cancellation token you have to change the signature
    • The method should receive a function which anticipates a CancellationToken and returns a Task
    • It should also receive the user cancellation token
  • In the ExecuteAsync call you "combine" the TimeoutPolicy and the user cancellation token
    • If the token asks for cancellation first then ExecuteAsync will throw a TaskCanceledException
    • If TimeoutPolicy asks for cancellation first then ExecuteAsync will throw a TimeoutRejectedException which is translated to an OperationCanceledException

The usage of this method looks like this:

await TimeoutAfter((token) => _webSocket.ConnectAsync(_uri, token), ct, OpenTimeoutMs);

...

await TimeoutAfter((token) => _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", token), ct, CloseTimeoutMs);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文