首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何取消对Polly的异步WaitAndRetryPolicy的挂起重试

如何取消对Polly的异步WaitAndRetryPolicy的挂起重试
EN

Stack Overflow用户
提问于 2019-07-26 07:45:48
回答 1查看 4.9K关注 0票数 1

我使用polly对HTTP请求进行n次简单重试。它应该处理任何异常并重新尝试将我的有效负载发送到api端点n次。因此,我使用了一个WaitAndRetryPolicy,它用悲观策略包装了一个TimoutPolicy,每次尝试都会超时。都是异步策略。

当重试案例发生时,进行的每一次重试尝试都会在连接重新配置后发布到端点。

将这两个策略封装在一起的方法:

代码语言:javascript
复制
    public static PolicyWrap WaitAndRetryNTimesWithTimeoutPerTry(int n, TimeSpan sleepDuration, TimeSpan retryTimeout)
    {
        var waitAndRetryPolicy = Policy.Handle<Exception>().WaitAndRetryAsync(
        retryCount: n,
            sleepDurationProvider: attempt => sleepDuration,
            onRetry: (exception, waitDuration, ctx) =>
            {
                Debug.WriteLine($"[Polly.OnRetry due '{exception.Message}'; waiting for {waitDuration.TotalMilliseconds} ms before retrying.");
            }
        );

        var timeoutPerTryPolicy = Policy.TimeoutAsync(
            retryTimeout, TimeoutStrategy.Pessimistic);

        return waitAndRetryPolicy.WrapAsync(timeoutPerTryPolicy);
    }

调用web的代码:

代码语言:javascript
复制
    var waitAndRetry5TimesWithShortTimeout = ResiliencePolicyFactory.WaitAndRetryNTimesWithTimeoutPerTry(
        n: 5,
        sleepDuration: TimeSpan.FromMilliseconds(700),
        retryTimeout: TimeSpan.FromMilliseconds(2300));
        }

    try
    {
        await waitAndRetry5TimesWithShortTimeout.ExecuteAndCaptureAsync(async token =>
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                response = await client.PostAsync(uri, content, cancellationToken);
                if (response.IsSuccessStatusCode)
                {
                    Debug.WriteLine($"[{nameof(CheckinService)}] ===>> Now Checked in!");
                }
            }
        }, cancellationToken);
    }
    catch(Exception ex)
    {
        throw new ApplicationException("NoCheckInPossible", ex);
    }

当代码命中重试案例并在几次重试后成功时,所做的每次重试尝试都会被发送到端点,尽管我将取消令牌传递给ExecuteAsync-Task和HttpClient。

据我理解,第一个成功请求应该取消所有挂起的重试。有人能指出我做错了什么吗?

EN

回答 1

Stack Overflow用户

发布于 2019-07-27 09:29:48

问题似乎是这句话:

代码语言:javascript
复制
response = await client.PostAsync(uri, content, cancellationToken);

使用一个名为cancellationToken的变量,而不是Polly在async token =>上传递给已执行的委托的变量token

使用下面的方法应该可以修复它:

代码语言:javascript
复制
response = await client.PostAsync(uri, content, token);

解释

Polly超时策略在任何cancellationToken中调用者传递给执行。,但要想使该超时令牌产生任何效果,在执行委托中,您必须使用Polly提供给执行的令牌(在本例中为变量token )。

(从问题中发布的代码中,我们看不到任何取消cancellationToken的信号;如果存在,请评论或编辑该问题以澄清。)

使用代码client.PostAsync(uri, content, cancellationToken),如果没有取消cancellationToken,那么每个帖子都不会被取消,这很可能解释了为什么会看到多个帖子运行到完成。

游行示威

为了演示,我制作了一个接近您发布的代码的可运行的可复制示例

代码语言:javascript
复制
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);
}

您可以在DotNetFiddle中运行这个并看到它通常提供的输出如下:

代码语言:javascript
复制
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!

(代码示例随机模拟不同程度的故障;您可能需要运行几次才能看到类似的结果。)

多个调用显然被放置(Placing call),多次运行到完成(Now Checked in!),因为没有什么可以取消它们。

更改指示使用token的行,可以看到,即使多个调用被放置,较早的尝试也会被取消,并且只有一个成功。

代码语言:javascript
复制
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!

整理

因为HttpClient.PostAsync(...)确实尊重CancellationToken,所以您可以使用稍微高效的TimeoutStrategy.Optimistic

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57215409

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档