简体   繁体   English

C#中具有超时功能的通用异步重试

[英]Generic async Retry with Timeout function in C#

I have hundreds of calls to various external API's that I want to wrap in a generic async function that is capable of doing retries and handling timeouts. 我有几百个调用各种外部API,我希望将其包装在一个能够重试和处理超时的通用异步函数中。

Basically, I think I need to implement something like this function call: 基本上,我认为我需要实现类似这个函数调用的东西:

await Retry(()=>someFunctionAsync(doWorkParams, new CancellationToken()), retryCount, timeout);

How do I define such Retry function? 如何定义此类重试功能? Also, how do I call this function from Sync code, as a ton of my calls are housed in the sync methods? 另外,我如何从同步代码中调用此功能,因为我的调用大量存在于同步方法中?

Don't reinvent the wheel. 不要重新发明轮子。 Just use Polly which supports exactly the scenario you're talking about as well as all kinds of scenarios for retry and advanced patterns like circuit breaker. 只需使用支持您正在讨论的场景的Polly ,以及各种重试场景和断路器等高级模式。

Here's an async example from their docs: 这是他们的文档的异步示例:

await Policy
  .Handle<SqlException>(ex => ex.Number == 1205)
  .Or<ArgumentException>(ex => ex.ParamName == "example")
  .RetryAsync()
  .ExecuteAsync(() => DoSomethingAsync());

How do I define such Retry function? 如何定义此类重试功能?

  1. Install Polly . 安装Polly
  2. Define a policy and use it. 定义策略并使用它。
  3. Profit. 利润。

Something like this should work: 这样的事情应该有效:

static async Task RetryAsync(Func<CancellationToken, Task> func, int retryCount, TimeSpan timeout)
{
  using (var cts = new CancellationTokenSource(timeout))
  {
    var policy = Policy.Handle<Exception>(ex => !(ex is OperationCanceledException))
        .RetryAsync(retryCount);
    await policy.ExecuteAsync(() => func(cts.Token)).ConfigureAwait(false);
  }
}

Also, how do I call this function from Sync code, as a ton of my calls are housed in the sync methods? 另外,我如何从同步代码中调用此功能,因为我的调用大量存在于同步方法中?

That's a totally different question. 这是一个完全不同的问题。 Blocking on asynchronous code always has pitfalls. 阻止异步代码总是有陷阱。 There is no solution that works for any arbitrary Func<Task> . 没有适用于任何Func<Task>解决方案。 See my MSDN article for a variety of hacks you can try, if you must do this. 如果您必须这样做,请参阅我的MSDN文章,了解您可以尝试的各种黑客攻击。 It would be better, though, to keep asynchronous code asynchronous, and synchronous code synchronous. 但是,保持异步代码异步和同步代码同步会更好。

If you are still curious about how to do it without Policy it would be something like this: 如果你仍然对如何在没有政策的情况下这样做感到好奇,那将是这样的:

/// Untested code
static class Retry
{
    public static async Task<T> Run<T>(Func<CancellationToken, Task<T>> function, int retries, TimeSpan timeout)
    {
        Exception error = null;
        do
        {
            try
            {
                var cancellation = new CancellationTokenSource(timeout);
                return await function(cancellation.Token).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                error = ex;
            }

            retries--;
        }
        while (retries > 0);

        throw error;
    }

    public static async Task<T> Run<T>(Func<Task<T>> function, int retries, TimeSpan timeout)
    {
        Exception error = null;
        do
        {
            try
            {
                var timeoutTask = Task.Delay(timeout);
                var resultTask = function();

                await Task.WhenAny(resultTask, timeoutTask).ConfigureAwait(false);

                if (resultTask.Status == TaskStatus.RanToCompletion)
                    return resultTask.Result;
                else
                    error = new TimeoutException();
            }
            catch (Exception ex)
            {
                error = ex;
            }

            retries--;
        }
        while (retries > 0);

        throw error;
    }
}

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

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