Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18633] ファイルの概要

このコミットは、Go言語の標準ライブラリである net/http パッケージにおいて、DefaultTransport が使用するTCP接続にTCP Keep-Alivesを有効にし、そのタイムアウト値を明示的に30秒に設定する変更を導入します。これにより、アイドル状態のHTTP接続がネットワーク機器によって不意に切断されることを防ぎ、接続の信頼性と効率を向上させます。

コミット

commit 49beb23ba73eab22c470e68cac87d51a0d12d97b
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Mon Feb 24 15:20:45 2014 -0800

    net/http: use TCP Keep-Alives on DefaultTransport's connections
    
    Update #3362
    
    Also set a 30 second timeout, instead of relying on the
    operating system's timeout, which if often but not always 3
    minutes.
    
    LGTM=crawshaw
    R=rsc, crawshaw
    CC=golang-codereviews
    https://golang.org/cl/68330046

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/49beb23ba73eab22c470e68cac87d51a0d12d97b

元コミット内容

net/http: DefaultTransport の接続でTCP Keep-Alivesを使用するように変更。 Issue #3362 を更新。 また、オペレーティングシステムのタイムアウト(多くの場合3分だが、常にそうとは限らない)に依存するのではなく、30秒のタイムアウトを設定する。

変更の背景

この変更の背景には、Goの net/http クライアントが、特に長時間アイドル状態の接続において、ネットワークインフラストラクチャ(ファイアウォール、ロードバランサーなど)によって接続が不意に切断される問題がありました。これは、TCP接続が一定期間アイドル状態になると、中間ネットワーク機器がその接続を「死んでいる」と判断し、セッションテーブルから削除してしまうために発生します。クライアント側から見ると、接続はまだ確立されているように見えますが、実際にデータを送信しようとすると、接続がリセットされたり、タイムアウトしたりするエラーが発生します。

Goの net/http.DefaultTransport は、HTTPクライアントがリクエストを送信するために使用するデフォルトのトランスポート層です。これには、接続の再利用(Keep-Alive)機能が含まれており、同じサーバーへの複数のリクエストで新しいTCP接続を確立するオーバーヘッドを避けることができます。しかし、この再利用されるアイドル接続が、前述のネットワーク機器による切断の対象となっていました。

以前は、TCP Keep-Alivesの設定はオペレーティングシステムに依存していました。多くのOSではデフォルトで数分(例えば3分)に設定されていますが、これは環境によって異なり、また、多くのネットワーク機器のアイドルタイムアウトよりも長いことがありました。そのため、OSのデフォルト設定に任せていると、Goアプリケーションが使用するHTTP接続が、OSがKeep-Aliveパケットを送信する前に中間機器によって切断されてしまう可能性がありました。

このコミットは、この問題を解決するために、DefaultTransport が使用するTCP接続に対して明示的にTCP Keep-Alivesを有効にし、そのタイムアウトを30秒というより短い値に設定することで、アイドル接続がネットワーク機器によって切断される前にKeep-Aliveパケットが送信されるようにします。これにより、接続の健全性が定期的に確認され、不意の切断が大幅に減少します。

前提知識の解説

TCP Keep-Alives

TCP Keep-Alivesは、TCP接続がアイドル状態(データが送受信されていない状態)であっても、その接続がまだ有効であることを確認するために使用されるメカニズムです。これは、接続の一方の端がクラッシュしたり、ネットワークケーブルが切断されたりした場合に、もう一方の端がそれを検出し、リソースを解放できるようにするために重要です。

TCP Keep-Alivesが有効になっていると、一定期間データが送受信されない場合、TCPスタックは小さな(通常はデータを含まない)Keep-Aliveパケットを相手に送信します。

  • 応答がある場合: 接続はまだ有効であると判断され、タイマーがリセットされます。
  • 応答がない場合: パケットの再送が試みられ、それでも応答がない場合は、接続が切断されたと判断されます。

Keep-Alivesは、特に長時間接続を維持する必要があるが、データの送受信が頻繁ではないアプリケーション(例: データベース接続、チャットアプリケーション、HTTP Keep-Alive接続など)で役立ちます。これにより、無効な接続に対してデータを送信しようとしてエラーになることを防ぎ、リソースの無駄遣いを避けることができます。

net/http パッケージと DefaultTransport

Go言語の net/http パッケージは、HTTPクライアントとサーバーの実装を提供します。 http.Client はHTTPリクエストを送信するための主要な構造体であり、その Transport フィールドは実際にリクエストを送信し、レスポンスを受信するメカニズムを定義します。 http.DefaultClient は、http.Client のデフォルトインスタンスであり、その Transport フィールドは http.DefaultTransport に設定されています。

http.DefaultTransport は、http.Transport 型のグローバルなインスタンスであり、HTTP/1.1の接続プール、プロキシ設定、TLS設定などをデフォルトで提供します。多くのGoアプリケーションでは、明示的に http.Clienthttp.Transport を設定しない限り、この DefaultTransport が使用されます。

http.Transport は、内部で net.Dialer を使用してTCP接続を確立します。この net.Dialer が、接続のタイムアウトやKeep-Alive設定を制御する重要なコンポーネントです。

net.Dialer

net.Dialer は、ネットワークアドレスへの接続を確立するためのオプションを提供する構造体です。これには、接続のタイムアウト、Keep-Alive設定、ローカルアドレスのバインド、DNSルックアップの制御など、様々な設定が含まれます。

  • Timeout フィールド: 接続確立の最大待機時間を設定します。この時間内に接続が確立されない場合、エラーが返されます。
  • KeepAlive フィールド: TCP Keep-Alivesを有効にするかどうか、およびアイドル状態の接続でKeep-Aliveパケットを送信する間隔を設定します。このフィールドがゼロより大きい値に設定されると、TCP Keep-Alivesが有効になり、指定された間隔でパケットが送信されます。

技術的詳細

このコミットは、net/http.DefaultTransportDial フィールドを、明示的に設定された net.Dialer のインスタンスに置き換えることで、TCP Keep-Alivesの動作を制御します。

以前の DefaultTransport の定義は、Dial フィールドを明示的に設定していませんでした。これは、Goの内部的なデフォルトのダイヤラが使用されることを意味し、そのダイヤラはTCP Keep-Alivesの設定をオペレーティングシステムに委ねていました。オペレーティングシステムのデフォルトのKeep-Aliveタイムアウトは、通常、数分(例えばLinuxでは75秒、Windowsでは2時間など)と長く、多くのネットワーク機器のアイドルタイムアウト(数十秒から数分)よりも長いことが一般的です。この不一致が、アイドル接続が中間機器によって切断される問題を引き起こしていました。

このコミットでは、DefaultTransportDial フィールドに新しい net.Dialer インスタンスを割り当てます。この net.Dialer は以下の2つの重要な設定を持ちます。

  1. Timeout: 30 * time.Second: これは、新しいTCP接続を確立する際のタイムアウトを30秒に設定します。これは、接続試行が長時間ブロックされるのを防ぐための標準的な設定です。
  2. KeepAlive: 30 * time.Second: これがこのコミットの主要な変更点です。この設定により、TCP Keep-Alivesが有効になり、接続がアイドル状態になってから30秒ごとにKeep-Aliveパケットが送信されるようになります。

この30秒という値は、多くの一般的なネットワーク機器のアイドルタイムアウト(通常は60秒以下)よりも短いため、Goクライアントが使用するHTTP接続が、中間機器によって切断される前に定期的に「生きている」ことを証明できるようになります。これにより、クライアントがアイドル状態の接続を再利用しようとした際に、すでに切断されているためにエラーが発生するシナリオが大幅に減少します。

この変更は、Goアプリケーションが外部のHTTPサービスと通信する際の信頼性と堅牢性を向上させます。特に、マイクロサービスアーキテクチャや、長時間にわたってHTTP接続を維持するアプリケーションにおいて、その効果は顕著です。

コアとなるコードの変更箇所

--- a/src/pkg/net/http/transport.go
+++ b/src/pkg/net/http/transport.go
@@ -30,7 +30,13 @@ import (
 // and caches them for reuse by subsequent calls. It uses HTTP proxies
 // as directed by the $HTTP_PROXY and $NO_PROXY (or $http_proxy and
 // $no_proxy) environment variables.
-var DefaultTransport RoundTripper = &Transport{Proxy: ProxyFromEnvironment}\n+var DefaultTransport RoundTripper = &Transport{\n+\tProxy: ProxyFromEnvironment,\n+\tDial: (&net.Dialer{\n+\t\tTimeout:   30 * time.Second,\n+\t\tKeepAlive: 30 * time.Second,\n+\t}).Dial,\n+}\n 
 // DefaultMaxIdleConnsPerHost is the default value of Transport's
 // MaxIdleConnsPerHost.

コアとなるコードの解説

変更は src/pkg/net/http/transport.go ファイルの DefaultTransport 変数の初期化部分にあります。

変更前:

var DefaultTransport RoundTripper = &Transport{Proxy: ProxyFromEnvironment}

変更前は、DefaultTransportTransport 構造体のインスタンスとして初期化されており、Proxy フィールドのみが ProxyFromEnvironment に設定されていました。Dial フィールドは明示的に設定されていなかったため、http.Transport の内部的なデフォルトのダイヤラが使用されていました。このデフォルトダイヤラは、TCP接続の確立時にオペレーティングシステムのデフォルトのKeep-Alive設定に依存していました。

変更後:

var DefaultTransport RoundTripper = &Transport{
	Proxy: ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).Dial,
}

変更後では、DefaultTransport の初期化に Dial フィールドが追加され、net.Dialer のインスタンスが設定されています。

  1. &net.Dialer{...}: これは、新しい net.Dialer 構造体のポインタを作成しています。
  2. Timeout: 30 * time.Second: この行は、net.DialerTimeout フィールドを30秒に設定しています。これは、TCP接続を確立する際の最大待機時間です。
  3. KeepAlive: 30 * time.Second: この行は、net.DialerKeepAlive フィールドを30秒に設定しています。これにより、TCP Keep-Alivesが有効になり、アイドル状態の接続に対して30秒ごとにKeep-Aliveプローブが送信されるようになります。
  4. ).Dial: net.Dialer 構造体には Dial メソッドがあり、これは func(network, address string) (net.Conn, error) というシグネチャを持つ関数です。この関数が http.TransportDial フィールドに割り当てられます。これにより、DefaultTransport が新しい接続を確立する際に、このカスタム設定された net.Dialer が使用されるようになります。

この変更により、DefaultTransport を使用するすべてのHTTPクライアントは、デフォルトで30秒のTCP Keep-Alivesが有効になった接続を使用するようになり、ネットワーク機器による不意の接続切断の問題が軽減されます。

関連リンク

参考にした情報源リンク