繁体   English   中英

具有功能的Polly重试策略不等待结果

[英]Polly Retry policy with Function is not waiting for result

我正在尝试将现有功能转换为Polly Retry策略

public static T Execute<T>(Func<T> getTask) where T : Task
{
    var retryCount = 3;
    while (retryCount-- > 0)
    {
        try
        {
            getTask().Wait();
            return getTask();
        } catch(Exception ex){
            // handle retry
        }
    }
}

转换为此

public static T Execute<T>(Func<T> func) where T : Task
{
    var task = func();
    Policy.Handle<HttpRequestException>()
        .Or<TimeoutException>()
        .WaitAndRetryAsync(
            3,
            retryAttempt => TimeSpan.FromSeconds(Math.Pow(5, retryAttempt)),
            (exception, timeSpan, retryCount, context) =>
            {
                //do some logging
            })
        .ExecuteAsync(func).Wait();
    return task;
}

测试代码是

var retryCount = 0;
var res = HttpRetryWrapper.Execute(() => Task.Factory.StartNew<string>(function: () =>
{
    if (++retryCount == 3)
    {
        return "fake";
    }
    throw new TimeoutException();
}));

当我断言res值时,我没有得到正确的结果。 调试将我追溯到Execution未正确等待结果的位置。

test function的调用次数正确。 但是,日志记录混乱了,最终结果没有fake结果

对于通过硬编码的Polly策略进行异步执行的辅助方法,其中执行通过Task<TResult>异步返回TResult类型,您可以采用:

public static Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> func) 
{
    return Policy.Handle<HttpRequestException>()
        .Or<TimeoutException>()
        .WaitAndRetryAsync(
            3,
            retryAttempt => TimeSpan.FromSeconds(Math.Pow(5, retryAttempt)),
            (exception, timeSpan, retryCount, context) =>
            {
                //do some logging
            })
        .ExecuteAsync<TResult>(func); // This is an async-await-eliding contraction of: .ExecuteAsync<TResult>(async () => await func());
    }

(由于每次使用的策略都是相同的,因此您可以考虑将策略存储在静态字段中,并且仅创建一次。)

注意:这(有意地)不符合您对原始问题的评论中所述的合同,因为它是坚不可摧的:

public static T Execute<T>(Func<T> func) where T : Task

where T : Task最初看起来吸引使用旨在与TaskTask<T>一起使用的异步方法或类似异步方法的子句。 乔恩·斯凯特(Jon Skeet) 在这里这里解释为什么它不适用于异步。 您建议的辅助方法签名不是自身异步的:

public static T Execute<T>(Func<T> func) where T : Task

但是,引入.ExecuteAsync(async () => await func()); 在您的示例代码中,出现了类似的问题。 为了很好地与async / await配合使用,Polly .ExecuteAsync(...)重载以两种主要形式存在:

(1) Task ExecuteAsync(Func<Task> func)

(2) Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> func)

编译器必须在编译时选择一个或另一个 :在您的示例代码中,它不能在运行时编译 (1)或(2)。 由于只知道T : Task因此选择(1),它返回Task 因此,您在评论@JamesFaix的答案时看到的错误: Cannot implicitly convert type Task to T

如果要使用这种形式的帮助程序模式,调用方可以将其用于TaskTask<TResult>返回调用,则必须声明两个:

class HttpRetryWrapper
{
    private static policy = Policy.Handle<HttpRequestException>()
        .Or<TimeoutException>()
        .WaitAndRetryAsync(
            3,
            retryAttempt => TimeSpan.FromSeconds(Math.Pow(5, retryAttempt)),
            (exception, timeSpan, retryCount, context) =>
            {
                //do some logging
            });

    public static Task ExecuteAsync(Func<Task> func) 
    {
        return policy.ExecuteAsync(func);
    }

    public static Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> func) 
    {
        return policy.ExecuteAsync<TResult>(func);
    }
}

最后,如果这是用于包装通过HttpClient发出的调用,则推荐的模式是将Polly策略放在DelegatingHandler ,如@MuhammedRehanSaeed的此处的答案所述。 ASP.NET Core 2.1支持使用IHttpClientFactory创建此类DelegatingHandler的简洁声明。

我想你想要的是

public static T Execute<T>(Func<T> func) where T : Task
{
    return Policy.Handle<HttpRequestException>()
        .Or<TimeoutException>()
        .WaitAndRetryAsync(
            3,
            retryAttempt => TimeSpan.FromSeconds(Math.Pow(5, retryAttempt)),
            (exception, timeSpan, retryCount, context) =>
            {
                //do some logging
            })
        .ExecuteAsync(func).Wait();
}

在您的代码示例中,您一次调用了func并返回了它,然后在定义和调用一个策略之间,但是没有返回调用该策略的结果。

如果您想避免Wait我想您也可以

public static T Execute<T>(Func<T> func) where T : Task
{
    return Policy.Handle<HttpRequestException>()
        .Or<TimeoutException>()
        .WaitAndRetryAsync(
            3,
            retryAttempt => TimeSpan.FromSeconds(Math.Pow(5, retryAttempt)),
            (exception, timeSpan, retryCount, context) =>
            {
                //do some logging
            })
        .ExecuteAsync(async () => await func());
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM