【问题标题】:Retrying HttpClient Unsuccessful Requests重试 HttpClient 不成功的请求
【发布时间】:2013-10-08 23:03:07
【问题描述】:

我正在构建一个给定 HttpContent 对象的函数,它将发出请求并在失败时重试。但是,我收到异常说 HttpContent 对象在发出请求后被释放。无论如何要复制或复制 HttpContent 对象,以便我可以发出多个请求。

 public HttpResponseMessage ExecuteWithRetry(string url, HttpContent content)
 {
  HttpResponseMessage result = null;
  bool success = false;
  do
  {
      using (var client = new HttpClient())
      {
          result = client.PostAsync(url, content).Result;
          success = result.IsSuccessStatusCode;
      }
  }
  while (!success);

 return result;
} 

// Works with no exception if first request is successful
ExecuteWithRetry("http://www.requestb.in/xfxcva" /*valid url*/, new StringContent("Hello World"));
// Throws if request has to be retried ...
ExecuteWithRetry("http://www.requestb.in/badurl" /*invalid url*/, new StringContent("Hello World"));

(显然我不会无限期地尝试,但上面的代码基本上就是我想要的)。

它会产生这个异常

System.AggregateException: One or more errors occurred. ---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Http.StringContent'.
   at System.Net.Http.HttpContent.CheckDisposed()
   at System.Net.Http.HttpContent.CopyToAsync(Stream stream, TransportContext context)
   at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at System.Threading.Tasks.Task`1.get_Result()
   at Submission#8.ExecuteWithRetry(String url, HttpContent content)

是否有复制 HttpContent 对象或重用它的方法?

【问题讨论】:

  • 其他答案很好实现重试。但这里真正的问题是因为您的 HttpContent 在您的帖子之后被处置。您需要在每次重试之前重新创建 StringContent。你不会有这样的 ObjectDisposedException
  • 没错,这里的异常是由于每次发帖后HttpClient都会处理HttpContent造成的。为每个 Polly 执行克隆 HttpContent 是解决方案。可以在here 找到一些可用的克隆扩展。

标签: c# dotnet-httpclient httpcontent


【解决方案1】:

不要实现包装HttpClient 的重试功能,而是考虑使用在内部执行重试逻辑的HttpMessageHandler 构造HttpClient。例如:

public class RetryHandler : DelegatingHandler
{
    // Strongly consider limiting the number of retries - "retry forever" is
    // probably not the most user friendly way you could respond to "the
    // network cable got pulled out."
    private const int MaxRetries = 3;

    public RetryHandler(HttpMessageHandler innerHandler)
        : base(innerHandler)
    { }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        HttpResponseMessage response = null;
        for (int i = 0; i < MaxRetries; i++)
        {
            response = await base.SendAsync(request, cancellationToken);
            if (response.IsSuccessStatusCode) {
                return response;
            }
        }

        return response;
    }
}

public class BusinessLogic
{
    public void FetchSomeThingsSynchronously()
    {
        // ...

        // Consider abstracting this construction to a factory or IoC container
        using (var client = new HttpClient(new RetryHandler(new HttpClientHandler())))
        {
            myResult = client.PostAsync(yourUri, yourHttpContent).Result;
        }

        // ...
    }
}

【讨论】:

  • 请注意,此解决方案不适用于瞬态超时。在这种情况下,似乎在 CancellationToken 上请求取消,这可能会导致抛出 TaskCanceledException。
  • 正如@Gabi 提到的,这不适用于超时。似乎SendAsync 代表一个single 请求操作,因此这不是实现重试机制的正确方法。外部方法效果更好。
  • 我实现了这一点,并在自己的脚下开枪,因为如前所述,它不适用于超时。你不应该像我一样忽视其他评论者,你应该听他们的。
  • 我刚刚尝试过这个,它对我来说很糟糕,因为对base.SendAsync 的调用正在处理传递给client.PostAsyncHttpContent。所以,IME,你的答案不起作用。 (我得到这个答案的唯一原因是避免复制内容才能重试!:-))
  • 在 .NET Core 中,HttpClient 不再单方面处置HttpContent,因此部分问题消失了。见:github.com/dotnet/corefx/pull/19082/files
【解决方案2】:

ASP.NET Core 2.1 答案

ASP.NET Core 2.1 added support 直接为Polly。这里UnreliableEndpointCallerService 是一个在其构造函数中接受HttpClient 的类。失败的请求将以指数回退重试,以便下一次重试在前一次之后的指数更长的时间内发生:

services
    .AddHttpClient<UnreliableEndpointCallerService>()
    .AddTransientHttpErrorPolicy(
        x => x.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(3, retryAttempt)));

另外,请考虑阅读我的博文"Optimally Configuring HttpClientFactory"

其他平台回答

此实现使用Polly 以指数回退重试,以便下一次重试在前一次之后的指数更长的时间内进行。如果由于超时而抛出HttpRequestExceptionTaskCanceledException,它也会重试。 Polly 比 Topaz 更容易使用。

public class HttpRetryMessageHandler : DelegatingHandler
{
    public HttpRetryMessageHandler(HttpClientHandler handler) : base(handler) {}

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken) =>
        Policy
            .Handle<HttpRequestException>()
            .Or<TaskCanceledException>()
            .OrResult<HttpResponseMessage>(x => !x.IsSuccessStatusCode)
            .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(3, retryAttempt)))
            .ExecuteAsync(() => base.SendAsync(request, cancellationToken));
}

using (var client = new HttpClient(new HttpRetryMessageHandler(new HttpClientHandler())))
{
    var result = await client.GetAsync("http://example.com");
}

【讨论】:

  • 自 2016 年 7 月起,Polly 还可以本地处理异常和结果的混合(即自动将某些结果代码视为失败)。因此,上面示例中对 StatusCode 的处理现在可以在 Polly Policy 中本地表达。 Polly readme shows examples
  • @ThiagoSilva 你能不能只在策略中添加一个`.Handle() 来处理取消?
  • retryCount 重命名为retryAttempt 可能会更好。因为前者意味着重试次数(始终为 5),而后者实际上是指当前的重试尝试,在您的示例中从 1 到 5。
  • 要将DelegatingHandler 方法与超时一起使用,请将总体超时(在所有尝试中)与每次尝试的超时区分开来。 HttpClient 上的 Timeout 属性HttpClient.Timeout 将作为所有重试组合的整体超时。要强制每次尝试超时,请使用包装在 WaitAndRetry 策略中的 Polly TimeoutPolicy。此外,Polly 的 TimeoutPolicy 会抛出 TimeoutRejectedException。然后 WaitAndRetry 策略应该处理 TimeoutRejectedException,而不是 TaskCanceledException。这将每次尝试超时与外部取消和所有重试超时区分开来。
  • @DennisWelu +1,没错。这正是 Polly 的 TimeoutPolicy 引发不同异常 (TimeoutRejectedException) 的原因,因此重试策略可以将其与用户取消区分开来。正如我们在 Polly + HttpClientFactory 文档中所建议的那样,您建议 Dennis(独立取消源)的方法实际上就是将 Polly RetryPolicy 与 TimeoutPolicy 嵌套使用的方法:github.com/App-vNext/Polly/wiki/…
【解决方案3】:

当前的答案在所有情况下都不会按预期工作,特别是在请求超时的非常常见的情况下(请参阅我的 cmets 那里)。

此外,它们实施了一种非常幼稚的重试策略 - 很多时候您想要一些更复杂的东西,例如指数退避(这是 Azure 存储客户端 API 中的默认设置)。

我在阅读related blog post 时偶然发现了TOPAZ(还提供了错误的内部重试方法)。这是我想出的:

// sample usage: var response = await RequestAsync(() => httpClient.GetAsync(url));
Task<HttpResponseMessage> RequestAsync(Func<Task<HttpResponseMessage>> requester)
{
    var retryPolicy = new RetryPolicy(transientErrorDetectionStrategy, retryStrategy);
    //you can subscribe to the RetryPolicy.Retrying event here to be notified 
    //of retry attempts (e.g. for logging purposes)
    return retryPolicy.ExecuteAsync(async () =>
    {
        HttpResponseMessage response;
        try
        {
            response = await requester().ConfigureAwait(false);
        }
        catch (TaskCanceledException e) //HttpClient throws this on timeout
        {
            //we need to convert it to a different exception
            //otherwise ExecuteAsync will think we requested cancellation
            throw new HttpRequestException("Request timed out", e);
        }
        //assuming you treat an unsuccessful status code as an error
        //otherwise just return the respone here
        return response.EnsureSuccessStatusCode(); 
    });
}

注意requester 委托参数。它应该HttpRequestMessage,因为您不能多次发送相同的请求。至于策略,这取决于您的用例。例如,瞬态错误检测策略可以很简单:

private sealed class TransientErrorCatchAllStrategy : ITransientErrorDetectionStrategy
{
    public bool IsTransient(Exception ex)
    {
        return true;
    }
}

关于重试策略,TOPAZ 提供了三种选择:

  1. FixedInterval
  2. Incremental
  3. ExponentialBackoff

例如,这是 Azure 客户端存储库默认使用的 TOPAZ:

int retries = 3;
var minBackoff = TimeSpan.FromSeconds(3.0);
var maxBackoff = TimeSpan.FromSeconds(120.0);
var deltaBackoff= TimeSpan.FromSeconds(4.0);
var strategy = new ExponentialBackoff(retries, minBackoff, maxBackoff, deltaBackoff);

更多信息请见http://msdn.microsoft.com/en-us/library/hh680901(v=pandp.50).aspx

编辑请注意,如果您的请求包含 HttpContent 对象,则每次都必须重新生成它,因为 HttpClient 也会处理该对象(感谢您发现 Alexandre Pepin )。例如() =&gt; httpClient.PostAsync(url, new StringContent("foo")))

【讨论】:

  • 或查看Polly。一个更轻量级且恕我直言的更清洁(相对于过度设计)的库来做同样的事情!
  • “你不能多次发送同一个请求”。你能扩展一下吗?我有一个委托处理程序可以做到这一点,它似乎工作得很好。我错过了什么吗?
  • @bornfromanegg 当我尝试传递 HttpRequestMessage 而不是像现在这样的委托时(通常看起来像 () =&gt; client.GetAsync(url))然后第一次尝试会起作用,但以下重试会失败,抛出关于无法重用相同请求消息的异常(我不记得确切的异常类型和措辞)
  • 博客目前已关闭,这里是网络存档的链接web.archive.org/web/20150117043128/http://blog.devscrum.net/…
【解决方案4】:

复制 StringContent 可能不是最好的主意。但简单的修改可以解决问题。只需修改函数并在循环内创建 StringContent 对象,如下所示:

public HttpResponseMessage ExecuteWithRetry(string url, string contentString)
{
   HttpResponseMessage result = null;
   bool success = false;
   using (var client = new HttpClient())
   {
      do
      {
         result = client.PostAsync(url, new StringContent(contentString)).Result;
         success = result.IsSuccessStatusCode;
      }
      while (!success);
  }    

  return result;
} 

然后调用它

ExecuteWithRetry("http://www.requestb.in/xfxcva" /*valid url*/, "Hello World");

【讨论】:

  • 这可行,但对于非字符串 httpcontent 类型确实不方便。像多部分形式等。
  • 同意,我也很想禁止内容的处置。但是您需要在某处构造内容对象,那么为什么不在给定函数内部呢? :)
  • httpcontent 对象的创建由我项目中的另一个组件管理,有时它并不重要,并且请求发出逻辑旨在尽可能通用和可重用。如果每次都创建内容,这有点不合时宜。我通过自己复制 httpcontext 来解决这个问题。
  • 这只会重试从服务器返回的带有一些状态代码的请求。很多时候情况并非如此,并且会抛出异常,例如超时。尽管使用外部包装器,但您的想法是正确的。
  • @VladL 您不应该在循环中处理和重新创建 HttpClient。 HttpClient(尽管它实现了 IDisposable)旨在在应用程序的生命周期中重复使用,否则它将打开一堆套接字。如果你在负载下运行,你最终会得到 SocketExceptions,这意味着你已经用完了套接字。
【解决方案5】:

这构建了已接受的答案,但增加了传递重试次数的能力,并增加了向每个请求添加非阻塞延迟/等待时间的能力。它还使用 try catch 来确保在发生异常后继续进行重试。最后,我添加了代码以在 BadRequests 的情况下跳出循环,您不想多次重新发送相同的错误请求。

public class HttpRetryHandler : DelegatingHandler
{
    private int MaxRetries;
    private int WaitTime;

    public HttpRetryHandler(HttpMessageHandler innerHandler, int maxRetries = 3, int waitSeconds = 0)
        : base(innerHandler)
    {
        MaxRetries = maxRetries;
        WaitTime = waitSeconds * 1000; 
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = null;
        for (int i = 0; i < MaxRetries; i++)
        {
            try
            {
                response = await base.SendAsync(request, cancellationToken);
                if (response.IsSuccessStatusCode)
                {
                    return response;
                }
                else if(response.StatusCode == HttpStatusCode.BadRequest)
                {
                    // Don't reattempt a bad request
                    break; 
                }
            }
            catch
            {
                // Ignore Error As We Will Attempt Again
            }
            finally
            {
                response.Dispose(); 
            }

            if(WaitTime > 0)
            {
                await Task.Delay(WaitTime);
            }
        }

        return response;
    }
}

}

【讨论】:

  • 这是在 Polly 内部为 TimeoutAsync 政策重新发明轮子
【解决方案6】:

使用 RestEase 和 Task,在多次调用(单例)中重用 httpClient 重试时,它会冻结并抛出 TaskCanceledException。 为了解决这个问题,需要在重试之前 Dispose() 失败的响应

public class RetryHandler : DelegatingHandler
{
    // Strongly consider limiting the number of retries - "retry forever" is
    // probably not the most user friendly way you could respond to "the
    // network cable got pulled out."
    private const int MaxRetries = 3;

    public RetryHandler(HttpMessageHandler innerHandler)
        : base(innerHandler)
    { }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        HttpResponseMessage response = null;
        for (int i = 0; i < MaxRetries; i++)
        {
            response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
            if (response.IsSuccessStatusCode) {
                return response;
            }

            response.Dispose();
        }

        return response;
    }
}

【讨论】:

    【解决方案7】:

    这是我使用 polly 实现的。

    nuget

    https://www.nuget.org/packages/Microsoft.Extensions.Http.Polly

    https://www.nuget.org/packages/Polly

    using Polly;
    using Polly.Extensions.Http;
    
    //// inside configure service
    services.AddHttpClient("RetryHttpClient", c =>
    {
        c.BaseAddress = new Uri($"{configuration["ExternalApis:MyApi"]}/");
        c.DefaultRequestHeaders.Add("Accept", "application/json");
        c.Timeout = TimeSpan.FromMinutes(5);
        c.DefaultRequestHeaders.ConnectionClose = true;
    
    }).AddPolicyHandler(GetRetryPolicy());
    
    //// add this method to give retry policy
    private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        return HttpPolicyExtensions
            //// 408,5xx
            .HandleTransientHttpError()
            //// 404
            .OrResult(msg => msg.StatusCode == HttpStatusCode.NotFound)
            //// 401
            .OrResult(msg => msg.StatusCode == HttpStatusCode.Unauthorized)
            //// Retry 3 times, with wait 1,2 and 4 seconds.
            .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
    }
    

    【讨论】:

      【解决方案8】:

      我在使用单元测试和集成测试时进行了尝试和工作。但是,当我实际从 REST URL 调用时,它卡住了。我找到了this interesting post,这解释了为什么它会卡在这条线上。

      response = await base.SendAsync(request, cancellationToken);
      

      解决方法是在末尾添加.ConfigureAwait(false)

      response = await base.SendAsync(request, token).ConfigureAwait(false);
      

      我还像这样在那里添加了创建链接令牌部分。

      var linkedToken = cancellationToken.CreateLinkedSource();
      linkedToken.CancelAfter(new TimeSpan(0, 0, 5, 0));
      var token = linkedToken.Token;
      
      HttpResponseMessage response = null;
      for (int i = 0; i < MaxRetries; i++)
      {
          response = await base.SendAsync(request, token).ConfigureAwait(false);
          if (response.IsSuccessStatusCode)
          {
              return response;
          }
      }
      
      return response;
      

      【讨论】:

        【解决方案9】:

        您还可以参阅为 .NET HttpClient 构建瞬态重试处理程序。 访问参考KARTHIKEYAN VIJAYAKUMAR帖子。

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        using System.Threading.Tasks;
        using System.Data.SqlClient;
        using System.Net.Http;
        using System.Threading;
        using System.Diagnostics;
        using System.Net;
        using Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling;
        
        namespace HttpClientRetyDemo
        {
            class Program
            {
                static void Main(string[] args)
                {
                    var url = "http://RestfulUrl";
                    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url);
        
                    var handler = new RetryDelegatingHandler
                    {
                        UseDefaultCredentials = true,
                        PreAuthenticate = true,
                        Proxy = null
                    };
        
                    HttpClient client = new HttpClient(handler);
                    var result = client.SendAsync(httpRequestMessage).Result.Content
                        .ReadAsStringAsync().Result;
        
                    Console.WriteLine(result.ToString());
                    Console.ReadKey();
        
                }
            }
        
            /// <summary>
            /// Retry Policy = Error Detection Strategy + Retry Strategy
            /// </summary>
            public static class CustomRetryPolicy
            {
                public static RetryPolicy MakeHttpRetryPolicy()
                {
                    // The transient fault application block provides three retry policies
                    //  that you can use. These are:
                    return new RetryPolicy(strategy, exponentialBackoff);
                }
            }
        
            /// <summary>
            /// This class is responsible for deciding whether the response was an intermittent
            /// transient error or not.
            /// </summary>
            public class HttpTransientErrorDetectionStrategy : ITransientErrorDetectionStrategy
            {
                public bool IsTransient(Exception ex)
                {
                    if (ex != null)
                    {
                        HttpRequestExceptionWithStatus httpException;
                        if ((httpException = ex as HttpRequestExceptionWithStatus) != null)
                        {
                            if (httpException.StatusCode == HttpStatusCode.ServiceUnavailable)
                            {
                                return true;
                            }
                            else if (httpException.StatusCode == HttpStatusCode.MethodNotAllowed)
                            {
                                return true;
                            }
                            return false;
                        }
                    }
                    return false;
                }
            }
        
            /// <summary>
            /// The retry handler logic is implementing within a Delegating Handler. This has a
            /// number of advantages.
            /// An instance of the HttpClient can be initialized with a delegating handler making
            /// it super easy to add into the request pipeline.
            /// It also allows you to apply your own custom logic before the HttpClient sends the
            /// request, and after it receives the response.
            /// Therefore it provides a perfect mechanism to wrap requests made by the HttpClient
            /// with our own custom retry logic.
            /// </summary>
            class RetryDelegatingHandler : HttpClientHandler
            {
                public RetryPolicy retryPolicy { get; set; }
                public RetryDelegatingHandler()
                    : base()
                {
                    retryPolicy = CustomRetryPolicy.MakeHttpRetryPolicy();
                }
        
        
                protected async override Task<HttpResponseMessage> SendAsync(
                    HttpRequestMessage request,
                    CancellationToken cancellationToken)
                {
                    HttpResponseMessage responseMessage = null;
                    var currentRetryCount = 0;
                    //On Retry => increments the retry count
                    retryPolicy.Retrying += (sender, args) =>
                    {
                        currentRetryCount = args.CurrentRetryCount;
                    };
                    try
                    {
                        await retryPolicy.ExecuteAsync(async () =>
                        {
                            responseMessage = await base.SendAsync(request, cancellationToken)
                                .ConfigureAwait(false);
                            if ((int)responseMessage.StatusCode > 500)
                            {
                                // When it fails after the retries, it would throw the exception
                                throw new HttpRequestExceptionWithStatus(
                                    string.Format("Response status code {0} indicates server error",
                                        (int)responseMessage.StatusCode))
                                {
                                    StatusCode = responseMessage.StatusCode,
                                    CurrentRetryCount = currentRetryCount
                                };
                            }// returns the response to the main method(from the anonymous method)
                            return responseMessage;
                        }, cancellationToken).ConfigureAwait(false);
                        return responseMessage;// returns from the main method => SendAsync
                    }
                    catch (HttpRequestExceptionWithStatus exception)
                    {
                        if (exception.CurrentRetryCount >= 3)
                        {
                            //write to log
                        }
                        if (responseMessage != null)
                        {
                            return responseMessage;
                        }
                        throw;
                    }
                    catch (Exception)
                    {
                        if (responseMessage != null)
                        {
                            return responseMessage;
                        }
                        throw;
                    }
                }
            }
        
            /// <summary>
            /// Custom HttpRequestException to allow include additional properties on my exception,
            /// which can be used to help determine whether the exception is a transient
            /// error or not.
            /// </summary>
            public class HttpRequestExceptionWithStatus : HttpRequestException
            {
                public HttpStatusCode StatusCode { get; set; }
                public int CurrentRetryCount { get; set; }
        
                public HttpRequestExceptionWithStatus()
                    : base() { }
        
                public HttpRequestExceptionWithStatus(string message)
                    : base(message) { }
        
                public HttpRequestExceptionWithStatus(string message, Exception inner)
                    : base(message, inner) { }
            }
        }
        

        【讨论】:

        • 链接的网址无效
        • 链接已修复。
        【解决方案10】:

        我几乎有同样的问题。 HttpWebRequest queueing library, which guarantees request delivery 我刚刚更新(参见 EDIT3)避免崩溃的方法,但我仍然需要通用机制来保证消息传递(或在未传递消息的情况下重新传递)。

        【讨论】:

          【解决方案11】:

          我有同样的问题并解决了。这是关于“StringContent”/“HttpContent”

          请查看 Amogh Natu 的博客,它可以帮助我解决这个问题

          这段代码的问题是,当第一次调用 PostAsync 时 成功但失败了,httpContent 对象就被释放了。这是作为 在 HttpClient 类中设计。请参阅此方法中的注释。 尽管这看起来很奇怪,但他们打算这样做,以便用户 不必明确地这样做,也可以避免相同的请求 多次发布。

          所以发生的情况是,当第一次调用失败时,httpContent 是 处置,然后由于我们有重试机制,它试图使帖子 再次调用,现在使用已处理的对象,因此这次调用 失败并出现 ObjectDisposedException。

          解决这个问题的一个简单方法是不使用变量来存储 httpContent ,而是在制作时直接创建 http 内容 称呼。像这样的。

          http://amoghnatu.net/2017/01/12/cannot-access-a-disposed-object-system-net-http-stringcontent-while-having-retry-logic/

          【讨论】:

          • 链接无效。
          【解决方案12】:

          添加一个同时使用 Polly + 重试策略 + 每次重试超时策略的答案,因为最佳答案没有解决这个问题:

          Policy
              .Handle<HttpRequestException>()
              .Or<TaskCanceledException>()
              .Or<TimeoutRejectedException>()
              .OrResult<HttpResponseMessage>(x => !x.IsSuccessStatusCode)
              .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(3, retryAttempt)))
              .WrapAsync(
                  Policy.TimeoutAsync(TimeSpan.FromSeconds(1), delegate (Context ctx, TimeSpan timeSpan, Task task)
                  {
                      // Do some on-timeout action 
                      return Task.CompletedTask;
                  })
              )
              .ExecuteAsync(() =>
              {
                  return httpclient.PostAsync(url, httpRequest);
              });
          

          【讨论】:

            【解决方案13】:
                    //Could retry say 5 times          
                    HttpResponseMessage response;
                    int numberOfRetry = 0;
                    using (var httpClient = new HttpClient())
                    {
                        do
                        {
                            response = await httpClient.PostAsync(uri, content);
                            numberOfRetry++;
                        } while (response.IsSuccessStatusCode == false | numberOfRetry < 5);
                    }
            return response;
            
            
            
                    .........
            

            【讨论】:

            • 处理特定异常总是更好,而不是这样做,最终会导致更多问题
            猜你喜欢
            • 1970-01-01
            • 2020-01-26
            • 1970-01-01
            • 1970-01-01
            • 2022-01-11
            • 1970-01-01
            • 2018-12-23
            • 2019-04-21
            相关资源
            最近更新 更多