具有功能的 Polly 重试策略不等待结果
Polly Retry policy with Function is not waiting for result
我正在尝试将现有函数转换为 Polly 重试策略
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
我想你想要的是
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());
}
对于通过硬编码 Polly 策略异步执行的辅助方法,其中异步执行 return 类型 TResult
,通过 Task<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
最初看起来很适合设计用于 Task
或 Task<T>
的 or async async-like 方法。 Jon Skeet 解释了 here and 为什么它不适用于异步。您建议的辅助方法签名本身不是异步的:
public static T Execute<T>(Func<T> func) where T : Task
但是,在您的示例代码中引入 .ExecuteAsync(async () => await func());
会强制出现类似的问题。 Polly .ExecuteAsync(...)
重载,正是为了与 async
/await
很好地配合,存在两种主要形式:
(1) Task ExecuteAsync(Func<Task> func)
(2) Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> func)
编译器必须在编译时select其中之一:它不能(在您的示例代码中)将其编译为 (1) 或 (2) 在运行时。因为它只知道 T : Task
它 selects (1),它 returns Task
。因此,您在对@JamesFaix 的回答的评论中看到错误:Cannot implicitly convert type Task to T
.
如果您想要这种形式的辅助模式,调用者可以将其用于 Task
或 Task<TResult>
-returning 调用,您必须同时声明:
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 的回答 here. ASP.NET Core 2.1 supports concise declaration for creating such DelegatingHandler
s using IHttpClientFactory 中所述。
我正在尝试将现有函数转换为 Polly 重试策略
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
我想你想要的是
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());
}
对于通过硬编码 Polly 策略异步执行的辅助方法,其中异步执行 return 类型 TResult
,通过 Task<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
最初看起来很适合设计用于 Task
或 Task<T>
的 or async async-like 方法。 Jon Skeet 解释了 here and
public static T Execute<T>(Func<T> func) where T : Task
但是,在您的示例代码中引入 .ExecuteAsync(async () => await func());
会强制出现类似的问题。 Polly .ExecuteAsync(...)
重载,正是为了与 async
/await
很好地配合,存在两种主要形式:
(1) Task ExecuteAsync(Func<Task> func)
(2) Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> func)
编译器必须在编译时select其中之一:它不能(在您的示例代码中)将其编译为 (1) 或 (2) 在运行时。因为它只知道 T : Task
它 selects (1),它 returns Task
。因此,您在对@JamesFaix 的回答的评论中看到错误:Cannot implicitly convert type Task to T
.
如果您想要这种形式的辅助模式,调用者可以将其用于 Task
或 Task<TResult>
-returning 调用,您必须同时声明:
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 的回答 here. ASP.NET Core 2.1 supports concise declaration for creating such DelegatingHandler
s using IHttpClientFactory 中所述。