• システム開発に関わる内容をざっくりと書いていく

C# HttpClient の使い回しとソケット枯渇

C# で new HttpClient() をリクエストごとに new / Dispose すると、しばらくして ソケット枯渇・断続的タイムアウト・SocketException が出ることがある。

この記事ではなぜ起きるかいまの定石(IHttpClientFactory)をざっくり整理する。


先に結論

  • リクエストごとに new HttpClient() しない
  • ASP.NET Core では IHttpClientFactoryAddHttpClient)を使う
  • 使い回しすぎる静的 1 個にも DNS 変更が追従しない問題がある → Factory が無難
  • 症状: 一時的に成功→負荷で失敗、TIME_WAIT 増加、断続タイムアウト

なぜ枯渇するのか(ざっくり)

  • HttpClient を Dispose しても、裏の接続がすぐ OS に返るとは限らない
  • 短時間に大量 new/Dispose するとポートが TIME_WAIT で埋まる
  • 結果として「たまに繋がらない」「タイムアウトが増える」

NG / OK

// NG: 都度 new
using var client = new HttpClient();
await client.GetAsync(url);

// OK: Factory(型指定クライアント例)
builder.Services.AddHttpClient<IGitHubClient, GitHubClient>(c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    c.Timeout = TimeSpan.FromSeconds(30);
});

public class GitHubClient(HttpClient http) : IGitHubClient
{
    public Task<string> GetAsync(string path)
        => http.GetStringAsync(path);
}

名前付きクライアントでも可: AddHttpClient("payments")factory.CreateClient("payments")

静的 HttpClient 1 個は?

「アプリで 1 個を使い回せ」も昔の助言。ただし DNS 更新が反映されにくいなどの理由で、今は IHttpClientFactory 推奨

切り分けのヒント

  • コードに new HttpClient( が散らばっていないか検索
  • 負荷時だけ失敗する → 枯渇・スレッド枯渇を疑う
  • 例外が SocketException / HttpRequestException / タイムアウト
  • コンテナ / 小さなマシンで再現しやすい

関連: HttpClient タイムアウト設定

ざっくりまとめ

  • 都度 new しない
  • ASP.NET Core なら AddHttpClient / IHttpClientFactory
  • タイムアウトやリトライは Factory で設定を集約

関連記事