繁体   English   中英

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

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

我有几百个调用各种外部API,我希望将其包装在一个能够重试和处理超时的通用异步函数中。

基本上,我认为我需要实现类似这个函数调用的东西:

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

如何定义此类重试功能? 另外,我如何从同步代码中调用此功能,因为我的调用大量存在于同步方法中?

不要重新发明轮子。 只需使用支持您正在讨论的场景的Polly ,以及各种重试场景和断路器等高级模式。

这是他们的文档的异步示例:

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

如何定义此类重试功能?

  1. 安装Polly
  2. 定义策略并使用它。
  3. 利润。

这样的事情应该有效:

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);
  }
}

另外,我如何从同步代码中调用此功能,因为我的调用大量存在于同步方法中?

这是一个完全不同的问题。 阻止异步代码总是有陷阱。 没有适用于任何Func<Task>解决方案。 如果您必须这样做,请参阅我的MSDN文章,了解您可以尝试的各种黑客攻击。 但是,保持异步代码异步和同步代码同步会更好。

如果你仍然对如何在没有政策的情况下这样做感到好奇,那将是这样的:

/// 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