HttpClient 的并发请求花费的时间比预期的要长

Com*_*Guy 5 c# dotnet-httpclient

我有一个同时接收多个请求的网络服务。对于每个请求,我需要调用另一个网络服务(身份验证)。问题是,如果同时发生多个(>20)请求,响应时间会突然变得更糟。

我制作了一个示例来演示该问题:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

namespace CallTest
{
    public class Program
    {
        private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler { Proxy = null, UseProxy = false });

        static void Main(string[] args)
        {
            ServicePointManager.DefaultConnectionLimit = 100;
            ServicePointManager.Expect100Continue = false;

            // warmup
            CallSomeWebsite().GetAwaiter().GetResult();
            CallSomeWebsite().GetAwaiter().GetResult();

            RunSequentiell().GetAwaiter().GetResult();

            RunParallel().GetAwaiter().GetResult();
        }

        private static async Task RunParallel()
        {
            var tasks = new List<Task>();
            for (var i = 0; i < 300; i++)
            {
                tasks.Add(CallSomeWebsite());
            }
            await Task.WhenAll(tasks);
        }

        private static async Task RunSequentiell()
        {
            var tasks = new List<Task>();
            for (var i = 0; i < 300; i++)
            {
                await CallSomeWebsite();
            }
        }

        private static async Task CallSomeWebsite()
        {
            var watch = Stopwatch.StartNew();
            using (var result = await _httpClient.GetAsync("http://example.com").ConfigureAwait(false))
            {
                // more work here, like checking success etc.
                Console.WriteLine(watch.ElapsedMilliseconds);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

顺序调用没有问题。它们需要几毫秒才能完成,并且响应时间基本相同。

然而,发送的请求越多,并行请求开始花费的时间就越长。有时甚至需要几秒钟。我在 .NET Framework 4.6.1 和 .NET Core 2.0 上进行了测试,结果相同。

更奇怪的是:我用 WireShark 跟踪 HTTP 请求,它们总是花费大约相同的时间。但示例程序报告的并行请求值比 WireShark 高得多。

如何才能获得相同的并行请求性能?这是线程池的问题吗?

Com*_*Guy 4

.NET Core 2.1 已修复此行为。我认为问题出在 HttpClient 使用的底层 Windows WinHTTP 处理程序上。

在.NET Core 2.1中,他们重写了HttpClientHandler(请参阅https://blogs.msdn.microsoft.com/dotnet/2018/04/18/performance-improvements-in-net-core-2-1/#user-content -联网):

在 .NET Core 2.1 中,HttpClientHandler 有一个新的默认实现,完全在其他 System.Net 库(例如 System.Net.Sockets、System.Net.Security 等)之上用 C# 从头开始​​实现。这不仅解决了上述问题行为问题,它显着提高了性能(该实现也作为 SocketsHttpHandler 公开公开,可以直接使用它而不是通过 HttpClientHandler 来配置 SocketsHttpHandler 特定的属性)。

事实证明,这消除了问题中提到的瓶颈。

在 .NET Core 2.0 上,我得到以下数字(以毫秒为单位):

Fetching URL 500 times...
Sequentiell   Total: 4209, Max:  35, Min: 6, Avg:  8.418
Parallel      Total:  822, Max: 338, Min: 7, Avg: 69.126
Run Code Online (Sandbox Code Playgroud)

但在 .NET Core 2.1 上,单个并行 HTTP 请求似乎有了很大改进:

Fetching URL 500 times...
Sequentiell   Total: 4020, Max:  40, Min: 6, Avg:  8.040
Parallel      Total:  795, Max:  76, Min: 5, Avg:  7.972
Run Code Online (Sandbox Code Playgroud)