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

Go: resp.Body は読み切ってから閉じる(keep-alive 接続を再利用させる)

defer resp.Body.Close() だけで、ボディを最後まで読まずに return すると、http.Transport がその接続を再利用できないことがある。毎回 TCP + TLS のハンドシェイクからやり直しになる。

@amitsinha__07 · 投稿 2026-10-14
#go#net-http#performance

書き方

go
resp, err := client.Do(req)
if err != nil {
    return err
}
defer func() {
    io.Copy(io.Discard, resp.Body) // 残りを読み捨てる
    resp.Body.Close()
}()

if resp.StatusCode != http.StatusOK {
    return fmt.Errorf("unexpected status: %s", resp.Status) // 読まずに早期 return しても上で読み切る
}
return json.NewDecoder(resp.Body).Decode(&out)

json.Decoder は JSON の値を読み終えたところで止まるので、末尾の改行などが残っていることがある。読み捨てておくと確実。

注意点

  • 巨大なボディや終わらないストリームを無制限に読み捨てると、そのほうが遅い・危険。io.Copy(io.Discard, io.LimitReader(resp.Body, 64<<10)) のように上限を付け、超えたら接続を捨てる判断もあり。
  • ステータスコードがエラーのときのボディも同じ。エラー処理の分岐で Close し忘れないよう、defer は err チェックの直後に置く。
  • 接続が再利用されているかは net/http/httptrace の GotConn(GotConnInfo.Reused)で確認できる。
  • HTTP/2 では1本の接続を多重化するので影響の出方は違うが、ボディを閉じるのは同じく必須。

出典

X の元の投稿を開く