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 高得多。
如何才能获得相同的并行请求性能?这是线程池的问题吗?
.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)