将内容复制到流时出错
本文关键字:出错 复制 | 更新日期: 2023-09-27 18:18:39
我正在使用。net Framework 4.5.2中的HttpClient类。
我对第三方web服务调用PostAsync。80%的时间这篇文章是有效的,20%的时间我们的回应被缩短了。在这种情况下,我们得到以下异常:
System.Net.Http。HttpRequestException: 复制内容到一个流。——比;System.IO.IOException:无法从传输连接:现有连接被强制关闭远端主机。——比;socketexception:一个已经存在的连接被远程主机强制关闭System.Net.Sockets.NetworkStream。BeginRead(Byte[] buffer, Int32offset, Int32大小,AsyncCallback,对象状态)——结束内部异常堆栈跟踪——atSystem.Net.Sockets.NetworkStream。BeginRead(Byte[] buffer, Int32offset, Int32大小,AsyncCallback回调,对象状态)System.Net.FixedSizeReader.StartReading ()System.Net.Security._SslStream。StartFrameHeader(Byte[] buffer, Int32offset, Int32 count, AsyncProtocolRequest, asyncRequest) atSystem.Net.Security._SslStream。StartReading(Byte[] buffer, Int32offset, Int32 count, AsyncProtocolRequest, asyncRequest) atSystem.Net.Security._SslStream。ProcessRead(Byte[] buffer, Int32offset, Int32 count, AsyncProtocolRequest, asyncRequest) atSystem.Net.TlsStream。BeginRead(Byte[] buffer, Int32 offset, Int32AsyncCallback的用法和样例System.Net.ConnectStream。BeginReadWithoutValidation (Byte[]缓冲区,Int32偏移量,Int32大小,AsyncCallback回调,对象状态)atSystem.Net.ConnectStream。BeginRead(Byte[] buffer, Int32 offset, Int32大小,AsyncCallback,回调,对象状态)System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.BeginRead (Byte []缓冲区,Int32偏移量,Int32计数,AsyncCallback回调,对象
后续相同的请求成功。
我们无法重试此请求,因为业务操作已被执行。所以这让我们陷入了尴尬的境地。
这是我的代码:
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = authorizationHeader;
HttpContent httpContent = new StringContent(someXml);
//Exception occurs on next line...
var response = await httpClient.PostAsync("https://thirdpartyendpoint", httpContent);
var responseXml = await response.Content.ReadAsStringAsync();
//convert to Dto
}
第三方服务成功地将记录保存到他们的数据库中,并且没有看到任何明显的异常。他们确实注意到,失败的请求通常比成功的请求需要更长的时间(大约18-30秒)来写入数据库。
我能做些什么来更好地处理这个问题?
我们通过2个代码修改解决了这个问题:
-
处理httpResponseMessage并使用简单的DTO
using (var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage)) { return await CreateDto(httpResponseMessage); }
-
将HTTP版本降级为v1.0
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri(url)) { Version = HttpVersion.Version10, Content = httpContent }; await client.SendAsync(httpRequestMessage);
,它的作用是添加Http头信息
Connection: close
而不是
Connection: keep-alive
我在使用共享HttpClient连接到服务器进行REST调用时遇到了类似的问题。问题最终是客户端和服务器上的KeepAlive超时不匹配。客户端超时由ServicePointManager上的MaxServicePointIdleTime设置,默认为100秒。在我们的服务器中,服务器端空闲超时设置为较短的值。
与客户端相比,服务器上的超时时间较短,导致服务器偶尔会在客户端试图连接时关闭连接。这导致了报告的异常。
注意,我最终发现了这个问题,因为我在相同的条件下也收到了这个异常:
System.Net.WebException: The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.
我有相同的错误(错误时复制内容到流)与HTTPClient PutAsync()方法:
using (StreamContent content = new StreamContent(stream))
{
HttpResponseMessage response = await client.PutAsync(url, content))
}
你需要指定HttpCompletionOption。ResponseHeadersRead标志在PutAsync中不可用,所以我切换到SendAsync:
using (StreamContent content = new StreamContent(stream))
{
var httpRequest = new HttpRequestMessage(HttpMethod.Put, url);
httpRequest.Content = content;
HttpResponseMessage response = await client.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead);
}