テック技報最新号へ →
コード

Go: 同じホストへ大量に呼ぶなら MaxIdleConnsPerHost を上げる(既定は 2)

http.DefaultTransport は 1 ホストあたり 2 本しかアイドル接続を残さない。同じ API へ並列 50 本で叩くと、バーストが終わるたびに 48 本が閉じられ、次のバーストで TCP + TLS のハンドシェイクをやり直すことになる。負荷に合わせた自前の http.Transport を使う。

@amitsinha__07 · 投稿 2026-10-19
#go#http#performance#networking

どんな時に気にする?

  • 決済 API・社内マイクロサービス・LLM API など、特定の 1 ホストへ高い並列度でリクエストする
  • バーストのたびにレイテンシが跳ねる、TIME_WAIT のソケットが大量に溜まる
  • httptrace で見ると GotConn の Reused=false が多い

設定例

go
// 起動時に 1 回だけ作り、使い回す(リクエストごとに Transport を作ると接続を一切再利用できない)
var apiClient = &http.Client{
    Timeout: 10 * time.Second,
    Transport: func() *http.Transport {
        t := http.DefaultTransport.(*http.Transport).Clone() // プロキシや TLS の既定値を引き継ぐ
        t.MaxIdleConns = 200        // 全ホスト合計のアイドル接続(既定 100)
        t.MaxIdleConnsPerHost = 64  // ホストごとのアイドル接続(既定 2)。想定並列度くらいに
        t.MaxConnsPerHost = 128     // 同時接続の上限(既定 0=無制限)。相手を潰さない保険
        t.IdleConnTimeout = 90 * time.Second
        return t
    }(),
}
  • MaxIdleConnsPerHost は「残しておく」本数で、同時に張れる本数の上限ではない。上限を付けたいときは MaxConnsPerHost。
  • MaxIdleConns が MaxIdleConnsPerHost より小さいと、そちらが先に効く。両方上げる。
  • 再利用されているかは net/http/httptrace の GotConnInfo.Reused で確かめられる。

注意点

  • 接続の再利用には、レスポンスボディを読み切って閉じることも必要(Go: resp.Body は読み切ってから閉じる(keep-alive 接続を再利用させる)※Go 1.27 で自動化)。
  • HTTP/2 で話す相手なら 1 本の接続で多重化されるので、この問題はほぼ起きない。効くのは HTTP/1.1 の相手(社内 API、プロキシ経由など)。
  • 値を上げすぎると、相手側のコネクション上限やロードバランサのアイドルタイムアウトに当たる。相手の keep-alive タイムアウトより IdleConnTimeout を短くしておくと、切れた接続を掴みにくい。

出典

X の元の投稿を開く