HttpClient 抛出 "An error occurred while sending the request."
HttpClient throwing "An error occurred while sending the request."
我有三层应用架构。
我的客户 --> 我的服务 A(IIS 中托管的 REST)--> 其他团队的服务 X(REST)。
服务 A 是 ASP.Net 4.6.1 框架,而不是 ASP.Net 核心。
客户端正在使用 HttpClient 与 A 通信,A 正在使用 HttpClient 与 X 通信。
客户对我的服务 A 和 X 发起了将近 2500 次调用。
在 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 --
这是我的服务电话。 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 post 建议可能是超时问题。所以我在客户端和服务 A 中添加了 600 秒。我还将 IIS 请求超时从默认的 2 分钟更改为 10(600 秒)。
我在生产环境中遇到了完全相同的问题。类似的设置和从客户端发送的大约 30k 个 http 调用。错误很少发生,而且很难重现。
阅读大量帖子后,我认为这是 Microsoft 在 HttpClient 中进行连接池时的错误(在我的解决方案中,我使用 httpClientFactory)。你可以看看https://github.com/dotnet/runtime/issues/26629
Microsoft 解决此问题之前采用的解决此问题的方法:
重试政策。已使用 Polly,当发生此异常时,将在几秒钟后重试调用。
请求超时增加到 3 分钟。
它现在似乎工作正常,但正如我之前提到的,很难以可控的方式重现错误。
经过一番挖掘,我解决了这个问题。
当 A 向 X 发送请求时,A 正在设置 Connection: keep-alive 而 X 正在响应 Connection: Close 属性 在 header.
所以在一些调用 A 被打开的 tcp 连接耗尽之后,它随机地抛出错误。
(Fiddler 帮我解决了这个问题)
所以我所要做的就是设置 HttpClient
的 ConnectionClose 属性
_client.DefaultRequestHeaders.ConnectionClose = true;
我有三层应用架构。
我的客户 --> 我的服务 A(IIS 中托管的 REST)--> 其他团队的服务 X(REST)。
服务 A 是 ASP.Net 4.6.1 框架,而不是 ASP.Net 核心。
客户端正在使用 HttpClient 与 A 通信,A 正在使用 HttpClient 与 X 通信。
客户对我的服务 A 和 X 发起了将近 2500 次调用。
在 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 --
这是我的服务电话。 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 post 建议可能是超时问题。所以我在客户端和服务 A 中添加了 600 秒。我还将 IIS 请求超时从默认的 2 分钟更改为 10(600 秒)。
我在生产环境中遇到了完全相同的问题。类似的设置和从客户端发送的大约 30k 个 http 调用。错误很少发生,而且很难重现。
阅读大量帖子后,我认为这是 Microsoft 在 HttpClient 中进行连接池时的错误(在我的解决方案中,我使用 httpClientFactory)。你可以看看https://github.com/dotnet/runtime/issues/26629
Microsoft 解决此问题之前采用的解决此问题的方法:
重试政策。已使用 Polly,当发生此异常时,将在几秒钟后重试调用。
请求超时增加到 3 分钟。
它现在似乎工作正常,但正如我之前提到的,很难以可控的方式重现错误。
经过一番挖掘,我解决了这个问题。 当 A 向 X 发送请求时,A 正在设置 Connection: keep-alive 而 X 正在响应 Connection: Close 属性 在 header.
所以在一些调用 A 被打开的 tcp 连接耗尽之后,它随机地抛出错误。
(Fiddler 帮我解决了这个问题)
所以我所要做的就是设置 HttpClient
的 ConnectionClose 属性_client.DefaultRequestHeaders.ConnectionClose = true;