【问题标题】:HttpClient Scalability Problems?HttpClient 可伸缩性问题?
【发布时间】:2014-08-08 03:22:35
【问题描述】:

我在 ASP.NET 应用程序中编写了这段代码

public class MyClass {
    public static MyClass() {
        ServicePointManager.DefaultConnectionLimit = 64;
        ServicePointManager.MaxServicePointIdleTime = 10000;
        ServicePointManager.Expect100Continue = false;
        ServicePointManager.CheckCertificateRevocationList = false;
    }

    Public Task<string> DoQuery(string a) {
        using(HttpClient client = new HttpClient(new HttpClientHandler(){UseDefaultCredentials = true}))
        {
            var response = await client.PostAsJsonAsync(MyWebAPI, new {A = a});
            if (response.IsSuccessStatusCode)
            {
                string result = await.response.Content.ReadAsStringAsync();
                return result;
            }
        }
    }                      

最初这段代码运行良好。我可以看到 Web 服务的调用没有问题,但是随着越来越多的用户访问我的服务器......突然我在我的日志中看到了很多这个错误的实例

   System.Net.Http.HttpRequestException: 
Error while copying content to a stream. ---> System.IO.IOException: 
The read operation failed, see inner exception. ---> 
System.Net.WebException: The request was aborted: The request was canceled.
       at System.Net.ConnectStream.BeginRead(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
       at System.Net.Http.DelegatingStream.BeginRead(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state)
       at System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.BeginRead(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state)

我很难发现这个问题的原因是它最初并没有出现,但只有在服务器加载时才会出现。这是否意味着我在使用 HttpClient 时遇到了某种限制?

你能帮我解决这个问题吗?

Edit:: 我得到的评论是,此代码调用的 Web 服务可能出错,因此我应该检查 Web 服务。我编写了一个负载客户端,并通过多个线程在 Web 服务上发送了 6000 个并发请求。网络服务中断并明确给出错误

Unable to connect to the remote server
An error occurred while sending the request.
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at SampleApp1.Program.<IsPermitted>d__1.MoveNext() in c:\myapps\SampleApp1\SampleApp1\Program.cs:line 58
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at SampleApp1.Program.<IsPermitted>d__1.MoveNext() in c:\myapps\SampleApp1\SampleApp1\Program.cs:line 58
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

因此,如果 Web 服务出现故障,那么它会给我上面的代码提供相同的错误消息……但事实并非如此。

另外,我上面的代码只会在几个请求(50 或 60)时出错,但即使有 5000 个并发请求,Web 服务也不会出错......所以我不认为这是一个 Web 服务问题.

【问题讨论】:

  • 为什么要调用Task.Run?为什么不等待 PostUsingClient()?
  • 我搜索了相同的错误消息。一个人使用这种方法解决了与我类似的问题......但它并没有帮助我。 stackoverflow.com/questions/18120315/…
  • 即使我删除 task.run 并在 PostUsingClient 上等待...它也是同样的问题。
  • 嗯,这里有很多问题.. 首先,您没有清理任何东西.. 您可能应该将这些 HttpClients 包装在 using 语句中,或者调用 Close 和/或 Dispose。您已将连接限制设置为 64,但您不断创建新客户端来创建新连接,但您从未关闭旧连接。最终,垃圾收集器将运行并关闭它们,但谁知道那会是什么时候。
  • 你还没有等待 PostAsJsonAsync

标签: asp.net .net httpclient


【解决方案1】:

查看堆栈跟踪,似乎问题可能与另一端的某些东西更相关。连接在读取时中止,而不是在连接时中止。如果您可以访问另一方,则应检查日志或找出可能导致其取消发送的原因。

您实际上可能遇到了线程池耗尽的问题。一般来说,HttpClient 不使用 ThreadPool 进行 Async I/O,但它确实需要 ThreadPool 来设置 Async I/O Send 操作,如果您同时有大量请求,那么您可能会筋疲力尽这个游泳池。

尝试通过性能监视器监控您的应用程序线程池的使用情况,看看发生了什么。

该问题也可能与使用 Http Keep Alives 的持久 Http 连接有关。您可以尝试禁用它们。

我还发现至少有一个人发现在经典模式下使用托管管道模式在 ASP.NET 应用程序中运行客户端会导致此问题。更改为集成模式为他们解决了这个问题。您是否在集成模式下运行?

【讨论】:

  • 我编写了一个基于控制台的测试客户端,它具有相同的 HttpClient 请求并启动了 6000 个并行线程来锤击 Web 服务。当 Web 服务无法承受任何负载时,它说无法连接到远程服务器发送请求时发生错误。在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务) 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
  • 但是上面的代码在 5 分钟内出错,只有 2 或 3 个用户。所以它不可能是网络服务的负载,因为那样我会收到上面的消息。
  • @KnowsNotMuch - 你有没有尝试我的任何建议?
  • @eric 使我的构造函数成为静态的。删除了额外的方法并直接在 PostAsJsonAsync 上等待
  • @KnowsNotMuch - 我指的是上面关于线程池、Keep Alives 和集成模式的建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-26
  • 1970-01-01
  • 2010-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-11
相关资源
最近更新 更多