Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/276.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从HttpClient SendAsync请求获取响应时出现无法解释的超时和延迟_C#_Async Await_Threadpool_Dotnet Httpclient - Fatal编程技术网

C# 从HttpClient SendAsync请求获取响应时出现无法解释的超时和延迟

C# 从HttpClient SendAsync请求获取响应时出现无法解释的超时和延迟,c#,async-await,threadpool,dotnet-httpclient,C#,Async Await,Threadpool,Dotnet Httpclient,我们有一个.NET4.7.2,它混合使用异步和同步代码(我知道这是个禁忌)。我们正在windows服务上使用NancyFX。该服务获取rest调用并进行rest调用。线程池看起来很健康(整个进程仅使用70个线程)。由于某些原因,一些http响应延迟10秒,有时延迟100秒,导致任务取消 下面是代码的结构 public async Task<Guid> SomeFunction() { ... var response = await _httpClient.SendAsy

我们有一个.NET4.7.2,它混合使用异步和同步代码(我知道这是个禁忌)。我们正在windows服务上使用NancyFX。该服务获取rest调用并进行rest调用。线程池看起来很健康(整个进程仅使用70个线程)。由于某些原因,一些http响应延迟10秒,有时延迟100秒,导致任务取消

下面是代码的结构

public async Task<Guid> SomeFunction()
{
   ...
   var response = await _httpClient.SendAsync(request, cancellationToken);
   ...
}
首先,我确信由于某种原因,网络上的响应被延迟了。但我们排除了多种方法,最重要的是通过perfview查看ETW跟踪,并看到数据包几乎立即返回(使用Microsoft Windows NDIS PacketCapture/PacketFragment)

其次,我确信这与异步方法上的.Result代码引起的线程池问题有关。然而,同样,进程上的线程保持稳定在70个线程。通过perfview,我可以看到饥饿确实没有发生(使用Microsoft Windows DotNETRuntime/ThreadPoolWorkerThreadAdjustment/Adjustment)

我还认为,可能我在使用wait/async和.Result时遇到了死锁,但死锁意味着请求永远不会完成,而不是延迟10秒

我还仔细检查了一下,我们只使用了一个httpclient实例,确实如此

还可能是什么

此时,我们将删除.Result并将其替换为适当的async/await。但我没有证据表明这会解决这个问题,因为我没有看到任何死锁或线程耗尽的迹象

下面是perfview分析

我们还考虑了一个建议,即我们正在耗尽http连接的某些容量。我认为情况并非如此的一个原因是,根据perfview,请求被发送出去,数据包返回,但响应并不构成c#堆栈。但是,这些性能计数器可能表明正在进行一些排队

更新 我们已经用它增加了http连接,它似乎已经生效了

<connectionManagement>
  <add address="*" maxconnection="1024"/>
</connectionManagement>

上面显示的排队已完全消失。然而,这些请求未完成的问题仍然存在

还可能是什么

您已经检查了我的第一个猜测,即线程池饥饿

还有一种可能性,这取决于API调用的完成方式。如果对同一主机同时有多个请求,则.NET网络堆栈可能会限制您。非ASP.NET应用程序的默认限制为同时向同一主机发送两个请求。在本例中,您有一个服务器应用程序,但没有ASP.NET应用程序,因此默认情况下,您会启用相当严格的节流功能

建议:将此代码放在启动中:

ServicePointManager.DefaultConnectionLimit = int.MaxValue;

请注意,.NET Core默认情况下不会限制客户端HTTP请求,因此这只是模拟现代.NET平台上的默认行为。

感谢所有帮助过的人。最后,没有什么是冒烟的枪。我无法证明线程饥饿或http连接饥饿。我们最终清理了异步/等待代码,删除了.Result之类的内容,并添加了一些缓存,问题就消失了


我最后的猜测是,当您混合使用async/Wait和.Result并发出大量请求时,.net 4.7.2中会出现一些边缘案例阻塞。

您正在创建多少个HttpClient实例?可能您的问题与API打开的最大连接数有关。请看这里:仅使用一个实例,并且不获取任何套接字异常以表明我们正在耗尽sockets。请尝试在应用程序的开始处添加以下语句:
ThreadPool.SetMinThreads(200200),看看是否有什么不同?这不是建议的修复方法,而是解决问题的一种方法。@TheodorZoulias我刚刚尝试过,这似乎减少了问题,但没有消除它。我需要等到明天早上再比较,但问题仍然存在。不过,如果这能解决问题,我会感到惊讶,因为在此更改之前,任务管理器的线程数一直徘徊在71(还有很大的增长空间)。现在线程徘徊在140,这也很奇怪,因为我要求它从200开始。仅供参考,
SetMinThreads
API没有立即创建指定数量的线程的效果。它只是通过创建一个新线程来确保后台工作的所有请求都能立即得到满足,直到达到这个阈值为止。在这之后,
ThreadPool
切换到一个保守的算法,在向池中注入一个新线程之前,它会等待500毫秒,等待当前正在运行的作业完成。你知道在尝试之前有没有办法证明这一点吗?有没有线索表明这就是正在发生的事情?我能把ServicePointManager.DefaultConnectionLimit甩了吗?如果是2或者什么的话,我就知道了?@Mark:我不知道是否有简单的方法来证明它。某个地方可能有诊断日志,但这是旧的.NET Framework代码,所以谁知道呢。我尝试了这个和那个,似乎减少了我在一些性能计数器中看到的排队(请参阅关于主要问题的更新),但没有消除问题。通信正在启动,请求一直在发送(远程服务器接收并响应),但进程无法正确接收。通常,处理响应的延迟是线程池耗尽的一个指标。
ServicePointManager.DefaultConnectionLimit = int.MaxValue;