HttpClient 抛出“发送请求时发生错误。”

Posted

技术标签:

【中文标题】HttpClient 抛出“发送请求时发生错误。”【英文标题】:HttpClient throwing "An error occurred while sending the request." 【发布时间】:2020-08-22 23:30:57 【问题描述】:

我有三层应用架构。

我的客户 --> 我的服务 A(REST 托管在 IIS 中)--> 其他团队的服务 X (REST)。

服务 A 是 ASP.Net 4.6.1 框架,而不是 ASP.Net Core。

Client 正在使用 HttpClient 与 A 通信,A 正在使用 HttpClient 与 X 通信。

客户端向我的服务发起了近 2500 次调用 A 和 X。

在 2500 次调用中,服务 A 随机(可能是 10 次调用)失败,但出现以下异常。它不可重现。

System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> 
System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a 
receive. ---> System.IO.IOException: Unable to read data from the transport connection: An     
established connection was aborted by the software in your host machine. ---> 
System.Net.Sockets.SocketException: An established connection was aborted by the software in your 
host machine
at System.Net.Sockets.Socket.BeginReceive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags 
socketFlags, AsyncCallback callback, Object state)
at System.Net.Sockets.NetworkStream.BeginRead(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback 
callback, Object state)
 --- End of inner exception stack trace ---
at System.Net.Security._SslStream.EndRead(IAsyncResult asyncResult)
at System.Net.TlsStream.EndRead(IAsyncResult asyncResult)
at System.Net.Connection.ReadCallback(IAsyncResult asyncResult)
--- End of inner exception stack trace ---
at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar)
--- End of inner exception stack trace --

这是我的服务 A 调用。 IIS 中的 A 调用下面的代码块并由每个请求调用。 X 正在获取用户凭据并根据用户返回数据,因此我们不会在调用之间共享 HttpClient。

var user = (System.Security.Principal.WindowsIdentity)HttpContext.Current.User.Identity;
            System.Security.Principal.WindowsIdentity.RunImpersonated(user.AccessToken, () =>
      
        static HttpClient Client = new HttpClient();
        static string CallX(string[] args)
        
            HttpClientHandler handler = new HttpClientHandler
            
                UseDefaultCredentials = true
            ;    

            Client = new HttpClient(handler)
            
                BaseAddress = new Uri("http://XserviceUrl/api/")
            ;
            Client.Timeout = TimeSpan.FromSeconds(600);    
            var result = Client.PostAsync("Fake X controller"
                , new StringContent(JsonConvert.SerializeObject(args)
                , Encoding.UTF8, "application/json")).Result;    

            result.EnsureSuccessStatusCode();

            var json = result.Content.ReadAsStringAsync().Result;
            return DosomethingWithResult(json);    
        
    );

我尝试过的事情:

建议的一些 SO 帖子可能是超时问题。所以我在客户端和服务 A 中添加了 600 秒。我还将 IIS 请求超时从默认的 2 分钟更改为 10(600 秒)。

【问题讨论】:

这种类型的错误几乎总是套接字状态错误。如果您运行 Wireshark 跟踪,您将看到对方先发送 fin 或 rst。这可能是由于流量过多、路由错误,甚至是服务器端问题。为了确保它是另一方,请确保您所有的出站都正确完成,您没有淹没服务器,并且对于任何挥之不去的会话,它们都已关闭。 Netstat 可以帮助您查看落后者。然而,最可能的原因是网络状况不佳。最好的查看位置是在服务器端,并找出为什么会话会立即开始 对方“Team's service X”呢,他们没看到什么错误吗? X 服务没有发现任何错误。这就是让复制或调查变得更加困难的原因。 您是否尝试过使其完全异步而不是阻塞异步代码?它可能导致死锁。超时是怎么回事,你检查失败请求的时间戳了吗?他们是否遇到了超时? 不要在每次调用时创建新的 HttpClient,这很可能是导致此问题的原因。 HttpClient 应该被创建一次并在您的应用程序的生命周期中重复使用。最多只创建其中的几个。 aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong 【参考方案1】:

经过一番挖掘,我解决了这个问题。 当 A 向 X 发送请求时,A 正在设置 Connection: keep-alive 并且 X 正在响应标头中的 Connection: Close 属性。

所以在一些调用之后,A 被打开的 tcp 连接耗尽并且随机抛出错误。

(Fiddler 帮我解决了这个问题)

所以我要做的就是设置 HttpClient 的 ConnectionClose 属性

_client.DefaultRequestHeaders.ConnectionClose = true;

【讨论】:

这对我有用,我收到了这个代码var httpMessage = await client.PostAsync(filenameURL, formContent);的错误【参考方案2】:

我在生产环境中遇到了完全相同的问题。类似的设置和大约 30k 从客户端发送的 http 调用。错误很少发生,而且很难重现。

阅读大量帖子后,我认为这是 Microsoft 在 HttpClient 中进行连接池时的错误(在我的解决方案中,我使用 httpClientFactory)。你可以看看https://github.com/dotnet/runtime/issues/26629

在 Microsoft 解决此问题之前,为解决此问题而采取的方法:

    重试策略。 Polly 已被使用,当发生此异常时,将在几秒钟后重试调用。

    将请求超时时间增加到 3 分钟。

目前看来工作正常,但正如我之前提到的,很难以可控的方式重现错误。

【讨论】:

我的服务是 ASP.Net Framework,所以我不确定如何使用 httpClientFactory 没关系。我想您使用的是静态 HttpClient。如果您将超时设置为 3 分钟并使用 Polly github.com/App-vNext/Polly/wiki/Retry 该解决方案已经过测试并且对我来说效果很好 @user781700 您是否尝试了重试策略并增加了超时?我实际上刚刚完成了一些负载测试,它在相当高的压力下工作得很好。如果您还有其他问题,请告诉我 我不认为增加超时是这里的问题。所有请求在不到 1 秒的时间内完成。在我介绍新的 API (Polly) 之前,我想知道为什么会这样。 @user781700 我们增加超时的原因是连接池中的错误将有足够的时间弹出。增加超时时间后,您将看到大约 2 分钟后您将进行重试。如果您尝试捕获错误,您将看到这是 TaskCanceledException(超时)。这与 httpclient 中的错误有关。

以上是关于HttpClient 抛出“发送请求时发生错误。”的主要内容,如果未能解决你的问题,请参考以下文章

C# HttpClient.SendAsync 在测试某些 URL 时抛出“发送请求时发生错误”异常

Azure 人脸检测 API - 发送请求时出错

发生一个或多个错误。(发送请求时发生错误)Mailchimp 列表集成

“发送请求时发生错误”,GetAsync(),Xamarin Forms

GetAsync 导致超时问题

解决httpclient抛出URISyntaxException异常