gpt4 book ai didi

asp.net-core - C# Httpclient Asp.net Core 2.0 on Kestrel 等待、锁定争用、CPU 高

转载 作者:行者123 更新时间:2023-12-02 08:24:21 25 4
gpt4 key购买 nike

我有一个非常简单的 API,它有一个单例实例,其中有许多在整个应用程序中重复使用的 HTTPClient。调用API时,我创建一个任务列表,每个任务调用一个客户端。我使用 CancellationToken 和另一个任务来实现严格的超时。

public class APIController : Controller
{
private IHttpClientsFactory _client;
public APIController(IHttpClientsFactory client)
{
_client = client;
}

[HttpPost]
public async Task<IActionResult> PostAsync()
{
var cts = new CancellationTokenSource();
var allTasks = new ConcurrentBag<Task<Response>>();
foreach (var name in list)//30 clients in list here
{
allTasks.Add(CallAsync(_client.Client[name], cts.Token));
}
cts.CancelAfter(1000);
await Task.WhenAny(Task.WhenAll(allTasks), Task.Delay(1000));
//do something with allTasks
}
}

CallAsync也很简单,只需使用客户端调用并等待应答即可。

 var response = await client.PostAsync(endpoint, content, token);

现在这段代码工作正常,1秒后返回,然后将取消请求发送到任何尚未返回的任务。任务列表大约有 30 个客户端,因此每次调用 API 都会调用 30 个端点,平均响应时间为 800ms。

该应用程序每秒管理 3000 个并发调用,因此每秒执行大约 100k Httpclient 调用

问题是 HttpClient 存在一些瓶颈,事实上 CPU 总是非常高,我需要大约 80 个(八个)16 核虚拟机和 32GB RAM 来处理流量。显然有什么问题。

我得到的一个提示是,在将我的 block 包更新到 Asp.net Core 2 之前,完全相同的代码执行得更好。

我对服务器进行了诊断,我的代码中没有任何错误,但看起来 HttpClients 客户端有点互相等待或卡住了。

enter image description here enter image description here

跟踪中确实没有其他内容。我正在使用工厂为每个端点创建单个实例:

  public class HttpClientsFactory : IHttpClientsFactory
{
public static Dictionary<string, HttpClient> HttpClients { get; set; }

public HttpClientsFactory()
{
HttpClients = new Dictionary<string, HttpClient>();
Initialize();
}

private static void Initialize()
{
HttpClients.Add("Name1", CreateClient("http://......"));
HttpClients.Add("Name2", CreateClient("http://...."));
HttpClients.Add("Name3", CreateClient("http://...."));

}

public Dictionary<string, HttpClient> Clients()
{
return HttpClients;
}

public HttpClient Client(string key)
{
try
{
return Clients()[key];
}
catch
{
return null;
}
}

public static HttpClient CreateClient(string endpoint)
{
try
{
var config = new HttpClientHandler()
{
MaxConnectionsPerServer = int.MaxValue,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
var client = new HttpClient(config)
{
Timeout = TimeSpan.FromMilliseconds(1000),
BaseAddress = new Uri(endpoint)
};

client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Connection.Clear();
client.DefaultRequestHeaders.ExpectContinue = false;
client.DefaultRequestHeaders.ConnectionClose = false;
client.DefaultRequestHeaders.Connection.Add("Keep-Alive");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

return client;
}
catch (Exception)
{
return null;
}
}
}

然后在启动中

 services.AddSingleton<IHttpClientsFactory, HttpClientsFactory>();

这里发生了什么,HttpClient 的单例不适合这种情况吗?我应该为每个线程创建一个 HttpClient 实例吗?我该怎么做?

更新

经过几天的测试,我确信 HTTPClient 调用期间的超时会导致一些连接打开,从而导致端口耗尽。关于如何避免这种情况有什么建议吗?

最佳答案

由于 HTTP 的设计工作方式,听起来您已经达到了操作系统处理 HTTP 请求的极限。如果您使用 .NET 框架(而不是 dotnet core),那么您可以使用 ServicePointManager 调整操作系统管理 HTTP 请求的方式。 。大多数 HTTP 服务器和客户端都使用 keep-alive,这意味着它们将为多个 HTTP 请求重用同一个 TCP 连接。您可以使用 ServicePointManager.FindServicePoint()函数获取 ServicePoint用于连接到特定主机的实例。对同一主机的每个 HTTP 请求都使用相同的 ServicePoint 实例。

尝试调整 ServicePoint 中的一些值,看看它如何影响您的应用程序。例如ConnectionLimit,它控制客户端和服务器之间将使用多少个并行 TCP 连接。而 keep-alive 可以让您为新的 HTTP 请求重用现有的 TCP 连接,以及 pipelined (使用 SupportsPipelining 检查您要连接的服务器是否支持此功能)允许您同时发送多个请求,HTTP 要求响应的顺序与请求的顺序相同。这意味着最初的缓慢响应将阻止所有后续请求/响应。通过拥有多个 TCP 连接,您可以并行地拥有多个非阻塞 HTTP 请求。但当然也有一个缺点,因为您现在有多个 TCP 连接,它们必须相互争夺网络资源。因此,调整这个值并看看它是否有所改善,但要小心!

如上所述,这里真正的问题可能是 HTTP 协议(protocol)以及它如何一次只能处理一个请求。幸运的是,HTTP/2 中有一个解决方案,不幸的是,asp.net 还没有很好地支持这一点。我还没有尝试过,因为我工作的系统还不支持它,但理论上它应该允许您并行发送和接收多个 HTTP 请求而不会阻塞。看看this thread它描述了一种让它工作的方法。

编辑

我完全错过了你正在 asp.net core 2.0 上运行。在这种情况下你 don't have access to ServicePointManager 。但如果您只需要在 Windows 上运行,那么您可以安装 WinHttpHandler nuget 并设置 MaxConnectionsPerServer属性(property)。但如果您使用的是 WinHttpHandler,那么我建议您尝试 HTTP/2,看看这是否会为您带来改善。

编辑2

我们刚刚发现一个问题,即 POST 请求花费的时间是 GET 请求的两倍,尽管请求的其余部分完全相同。当将 localhost 连接到本地服务器时,我们从异地位置发现了该问题,因此 ping 值比正常情况下高得多。这表明在执行 POST 时进行了两次往返,而在执行 GET 时仅进行了一次往返。解决方案是这两行:

ServicePointManager.UseNagleAlgorithm = false;
ServicePointManager.Expect100Continue = false;

也许这对你也有帮助?

关于asp.net-core - C# Httpclient Asp.net Core 2.0 on Kestrel 等待、锁定争用、CPU 高,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46086293/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com