質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

1回答

2477閲覧

HttpClient を間隔をあけて連続使用すると応答がなく固まる

t.kusu

総合スコア21

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

1クリップ

投稿2022/09/23 11:26

編集2022/09/23 12:38

前提

・WebAPI を利用したプログラムを作成しています。
・この通信のために HttpClient を使用しています。
・この通信は間隔をあけて数回程度、連続して利用することがあります。
・何度か通信を行うと、HttpClient.SendAsync を呼び出したところで応答がなく、そのままの状態が続きます。このとき例外等は発生しません。

実現したいこと

・複数回利用しても HttpClient.SendAsync で固まることなく応答を得られるようにしたい。

発生している問題・エラーメッセージ

・例外やその他のエラーを捕捉出来ていません。ただ固まっているだけのように見えます。

該当のソースコード

以下のコードは説明に不要な部分を除去するなどしているので、不備などがあるかもしれませんがご容赦ください。
実行環境は .NET Framework 4.8 & MVC です。

C#

1static HttpClient client = new HttpClient(); 2 3// var header = new Dictionary< string, string >() { { "param1", "data1" } }; 4// var body = new Dictionary< string, string >() { { "param2", "data2" } }; 5 6void test( string url, Dictionary< string, string > header, Dictionary< string, string > body ) 7{ 8 var request = new HttpRequestMessage( HttpMethod.Post, new System.Uri( url ) ); 9 request.Content = new FormUrlEncodedContent( body ); 10 foreach( var item in header ) request.Headers.Add( item.Key, item.Value ); 11 12 var response = await client.SendAsync( request ); 13 var stream = await response.Content.ReadAsStreamAsync(); 14 15 // stream から応答を受け取って何らかの処理を実施 16}

試したこと

・HttpClient はインスタンスを使い回すとのことなので static 変数にインスタンスを生成して利用していますが、毎回インスタンスを生成するようにしたり、using で囲ったりしましても結果は変わりません。(このような実装が悪影響を及ぼすことは承知しています。変化をみようと試しました。)
・その他の HttpClient に関係するインスタンスのうち IDisposable を継承しているものについて、using で囲ってみましたが、結果は変わりません。
・「何らかの処理を実施」の処理内容を、おもに実行時間が変わるものに変えると、繰り返して使用出来る = 応答がある回数が変わるようです。ただ、その時間が回数に比例などするのか、明確な関連性をつかめていません。

補足情報(まとめ)

しばらく悩んでいたのですが、なにが悪いのか、原因をつかむことが出来ませんでした。
お知恵をお貸しいただければ幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/09/24 00:07

質問者さん、追加の情報を回答のコメント欄に書いたので、それを試して結果を連絡するとか、何らかのフィードバックを返してください。
退会済みユーザー

退会済みユーザー

2022/09/28 01:36

追加情報の件「週明けに試してみます」とのことでしたが状況はいかがでしょう?
退会済みユーザー

退会済みユーザー

2022/09/30 21:59

Teratail は「お互いに知識や情報を交換・共有する場所」「価値のあるコンテンツを世の中に残し届ける事を目的としています」ということだそうです。ここを利用させてもらっている以上、結論(価値のあるコンテンツ)を出してクローズしてもらえることを期待します。
guest

回答1

0

HttpClient はインスタンスを使い回すとのことなので static 変数にインスタンスを生成して利用していますが、毎回インスタンスを生成するようにしたり、using で囲ったりしましても結果は変わりません。

「毎回インスタンスを生成するようにしたり、using で囲ったり」といいのはやってはいけないことです。

HttpClient のインスタンスを生成すると、そのたびにソケットも生成されます。しかし、HttpClient のインスタンスを Dispose してもソケットはすぐにはクローズされないので(デフォルトで 4 分かかるそう)、短期間で何度も生成 / Dispose を繰り返すとソケットの枯渇につながるという問題があり、それを避けるため、HttpClient のインスタンスはシングルトンにしてアプリで使いまわすということが推奨されています。

HttpClient の初期化と Dispose を繰り返すようなことをすると、socket が浪費されるという話については以下の記事を見てください。

YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE
https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

何を作っているか書いてないので不明ですが、Core 2.1 以降の ASP.NET Core Web アプリなら、以下のドキュメントに書いてあるように IHttpClientFactory を利用する手段があります。

ASP.NET Core で IHttpClientFactory を使用して HTTP 要求を行う
https://learn.microsoft.com/ja-jp/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.1

Windows Forms アプリなどでも、IHttpClientFactory を利用して HTTP 接続プールを作り、それから HttpClient インスタンスを取得してアクセスする手段はあります。詳しくは以下の記事を見てください。

Windows Forms で IHttpClientFactory 利用 (CORE)
http://surferonwww.info/BlogEngine/post/2021/03/12/how-to-use-ihttpclientfactory-in-windows-forms-application.aspx

ただし、そもそも短い期間で一日に数回程度しか HttpClient は使わない Windows Forms アプリであれば、上の記事のように IHttpClientFactory を DI して使う必要はないかもしれません。

なので、単純に static と宣言して使い回す方が正解かもしれません。

投稿2022/09/23 11:53

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

t.kusu

2022/09/23 12:36

ありがとうございます。 開発環境を書き忘れていましたね。申し訳ありません。 過去資産の関係で .NET Framework 4.8 で C# MVC です。これは質問を修正して直しておきます。 このためせっかく記事を紹介いただいたのですが、利用出来ないようです。ほんとうにごめんなさい。 using 不可は承知しています。このためそうしないように実装していたのですが、それで問題が起こっていたので思いつく範囲のことをして変化を探ってみようと考えていました。これも描き方が悪かったかもしれませんね。 もし他にもお心当たりがあるようでしたら、お気を悪くなさらず教えていただけると幸いです。
退会済みユーザー

退会済みユーザー

2022/09/23 12:51 編集

.NET Framework 版の ASP.NET Web アプリでの対処方法は以下の Microsoft のドキュメントに書かれています。 Improper Instantiation antipattern https://learn.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/ そのドキュメントには、コントローラーに、 private static readonly HttpClient httpClient; という static フィールドを設けて、コントローラーのコンストラクタで、 httpClient = new HttpClient(); とすると書いてあります。しかし、コントローラーのコンストラクタはクライアントから要求を受けるたびに呼び出されるので、要求を受けるたびに HttpClient のインスタンスを新たに作るということになってしまうと思うのですが・・・  何故それが問題ないのか理解し難いです。 でも、まぁ、Microsoft のドキュメントですし、検証したようですし、.NET Framework 版の ASP.NET アプリでは他に適当な手はなさそうですし、もし問題が起きたら Microsoft のせいにできるので(笑)、その方法を使ってみるのが良いかもしれません。
t.kusu

2022/09/24 02:18

ありがとうございます。 ご紹介いただいた Microsoft の記事を読みました。・・・英語は苦手なので拾い読みですが。 そこに書かれていたサンプル?コードでは静的コンストラクタで HttpClient のインスタンスを生成しているようですね。 フィールドに直書きとどう違うんだろうという気はしますが、週明けに試してみます。 もしほかにもお心当たりがあれば、指摘をいただけると嬉しいです。 よく思い出してみると、前の返信でもいただいた Dispose した時のソケットを初期化するまで(デフォルトで)4分かかるというのに関連してそうにも思えます。でも Dispose や Close などの捜査はしていないので謎なのですが。
t.kusu

2022/10/02 02:24

またも遅くなりました。 「静的コンストラクタでインスタンスを生成する」を試したのですが、結果は変わりませんでした。 どこが違うんだろう。。
退会済みユーザー

退会済みユーザー

2022/10/02 02:45

Microsoft のドキュメントが間違っているか、そうでなければ質問者さんがドキュメントに書いてある通り実装できてないかのいずれかしか思い当たることはないです。 個人的には前者が怪しいと思っているのですが、質問者さんが試した結果がそれを裏付けているのかもしれませんね。ASP.NET Web アプリは典型的なマルチスレッドアプリで、サーバーはクライアント型の要求を受けるとスレッドプールからスレッドを取得し、そのスレッドで要求を処理して応答を返すという仕組みになってます。 紹介した記事には "The HttpClient class is designed to be shared rather than pooled." とか "Objects that you share across multiple requests must be thread-safe. The HttpClient class is designed to be used in this manner" と書いてはあるのですが、記事のコードのように使ってそのあたりは OK なのか、試したこともないので自分には分かりません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問