.Net 4.5.2フレームワークでHttpClientクラスを使用しています。サードパーティのWebサービスに対してPostAsyncを実行しています。この投稿が機能する時間の80%、応答が短くなる時間の20%。この状況では、次の例外が発生します。
System.Net.Http.HttpRequestException:コンテンツをストリームにコピー中にエラーが発生しました。 ---> System.IO.IOException:トランスポート接続からデータを読み取れません:既存の接続が強制的に閉じられましたリモートホスト。 ---> System.Net.Sockets.SocketException:既存の接続は、System.Net.Sockets.NetworkStream.BeginRead(Byte []バッファー、Int32オフセット、Int32サイズ、AsyncCallbackコールバック、オブジェクト状態)でリモートホストによって強制的に閉じられました---内部例外スタックトレースの終わり--- System.Net.FixedSizeReader.StartReading()のSystem.Net.Sockets.NetworkStream.BeginRead(Byte []バッファー、Int32オフセット、Int32サイズ、AsyncCallbackコールバック、オブジェクト状態) System.Net.Security._SslStream.StartFrameHeader(Byte [] buffer、Int32 offset、Int32 count、AsyncProtocolRequest asyncRequest)at System.Net.Security._SslStream.StartReading(Byte [] buffer、Int32 offset、Int32 count、AsyncProtocolRequest asyncRequest) System.Net.Security._SslStream.ProcessRead(Byte [] buffer、Int32 offset、Int32 count、AsyncProtocolRequest asyncRequest)at System.Net.TlsStream.BeginRead(Byte [] buffer、Int32 offset、Int32 size、AsyncCallback asyncCallback、Object asyncState) )System.Net.ConnectStream.BeginReadWithoutValidation(Byte []でSystem.Net.ConnectStream.BeginRead(Byte []バッファ、Int32オフセット、Int32サイズ、AsyncCallbackコールバック、オブジェクト状態)のバッファ、Int32オフセット、Int32サイズ、AsyncCallbackコールバック、オブジェクト状態)System.Net.Http.HttpClientHandler.WebExceptionWrapperStreamでSystem.Net.Http.StreamToStreamCopy.StartRead()での.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
REST呼び出しでサーバーに接続する共有HttpClientを使用する場合、同様の問題が発生しました。問題は、クライアントとサーバーの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))
}
Put-Asyncでは使用できないHttpCompletionOption.ResponseHeadersReadフラグを指定する必要があるため、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);
}