[インデックス 17442] ファイルの概要
このコミットは、Go言語の標準ライブラリであるnet
パッケージにaddrList
という新しい型を追加するものです。この型は、複数のIPアドレスを効率的に管理し、特にTCP接続の確立時における高速フェイルオーバー(Happy Eyeballs)をサポートするための準備として導入されました。
コミット
commit 20692c22d7407125dba801dadee7413c6bc6b0a6
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Fri Aug 30 09:28:26 2013 +0900
net: add addrList
This CL adds a new type addrList that will carry a short list of IP
addresses to dial helper functions in the upcoming CLs.
This is in preparation for TCP connection setup with fast failover on
dual IP stack node as described in RFC 6555.
Update #3610
Update #5267
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13241046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/20692c22d7407125dba801dadee7413c6bc6b0a6
元コミット内容
net: add addrList
この変更は、今後のCL(Change List)でダイヤルヘルパー関数に短いIPアドレスのリストを渡すための新しい型addrList
を追加します。
これは、RFC 6555で記述されているデュアルIPスタックノードでの高速フェイルオーバーを伴うTCP接続設定の準備です。
Issue #3610 を更新 Issue #5267 を更新
変更の背景
このコミットの主な背景は、現代のネットワーク環境におけるデュアルスタック(IPv4とIPv6の両方をサポートする)ホストでの接続確立の課題に対処することです。特に、IPv6の導入が進む中で、クライアントがIPv4とIPv6の両方のアドレスを持つサーバーに接続しようとする際に、どちらか一方のプロトコルが利用できない、またはパフォーマンスが悪い場合に、接続がタイムアウトするまで待つという問題がありました。
この問題を解決するために、「Happy Eyeballs」(RFC 6555)と呼ばれるアルゴリズムが提案されました。Happy Eyeballsは、クライアントがIPv4とIPv6の両方のアドレスに対してほぼ同時に接続を試み、最初に成功した接続を使用することで、ユーザー体験を向上させることを目的としています。これにより、片方のプロトコルに問題があっても、もう一方のプロトコルで迅速に接続を確立できるようになります。
このコミットで導入されたaddrList
型は、このHappy EyeballsアルゴリズムをGoのnet
パッケージに実装するための基盤となります。dial
ヘルパー関数が複数のアドレスを効率的に処理できるように、アドレスのリストを保持する構造が必要とされたためです。
コミットメッセージで参照されているIssue #3610と#5267は、GoにおけるIPv6のサポートと、デュアルスタック環境での接続確立の改善に関する議論を示唆しています。
-
Issue #3610: net: IPv6 support for Dial このIssueは、Goの
net.Dial
関数がIPv6アドレスを適切に処理できるようにすることに関するものです。当時のGoのネットワークスタックはIPv4に重点を置いており、IPv6のサポートが不十分であったため、IPv6環境での接続に問題が生じていました。 -
Issue #5267: net: Happy Eyeballs (RFC 6555) for Dial このIssueは、まさにHappy EyeballsアルゴリズムをGoの
net.Dial
に実装することの必要性を議論しています。ユーザーは、IPv4とIPv6のどちらか一方の接続が遅延したり失敗したりした場合に、アプリケーションがフリーズするのを避けるために、この機能が求められていました。
これらの背景から、addrList
の導入は、Goのネットワーク機能が現代のデュアルスタック環境に適合し、より堅牢で高速な接続確立を提供するための重要なステップであったと言えます。
前提知識の解説
1. デュアルスタック(Dual-Stack)
デュアルスタックとは、ネットワークデバイスやホストがIPv4とIPv6の両方のプロトコルスタックを同時にサポートし、両方のアドレスを割り当てられている状態を指します。これにより、単一のデバイスがIPv4ネットワークとIPv6ネットワークの両方に接続できるようになります。インターネットがIPv4からIPv6へ移行する過渡期において、両方のプロトコルが共存する環境でシームレスな通信を実現するために不可欠な技術です。
2. Happy Eyeballs (RFC 6555)
Happy Eyeballsは、正式には「Successive Connections with Bounded Delays」として知られるアルゴリズムで、デュアルスタック環境における接続確立のユーザー体験を向上させるために設計されました。
問題点: クライアントがドメイン名を解決すると、通常、複数のIPアドレス(IPv4とIPv6の両方)が返される可能性があります。従来の接続確立では、クライアントは通常、返されたアドレスのリストの最初のものから順に接続を試みます。もし最初のIPアドレス(例えばIPv6アドレス)への接続が何らかの理由で遅延したり失敗したりした場合、クライアントはタイムアウトするまで待機し、その後次のアドレス(例えばIPv4アドレス)を試します。この待機時間は、ユーザーにとってアプリケーションの応答が遅れる原因となり、不満につながります。
Happy Eyeballsの解決策: Happy Eyeballsは、この問題を解決するために、複数のIPアドレス(特にIPv4とIPv6)に対してほぼ同時に接続を試みるアプローチを取ります。具体的には、最初のアドレス(例えばIPv6)への接続を試み、短い遅延(例えば50ミリ秒)の後に、まだ最初の接続が確立されていない場合、次のアドレス(例えばIPv4)への接続も開始します。これにより、どちらか一方の接続が先に成功すれば、その接続が使用され、もう一方の接続は破棄されます。
この「競合」によって、ユーザーは最も早く確立された接続の恩恵を受けることができ、片方のプロトコルに問題があっても、接続の遅延や失敗を最小限に抑えることができます。RFC 6555は、このアルゴリズムの詳細な動作と推奨される実装ガイドラインを提供しています。
3. netaddr
インターフェース
Goのnet
パッケージには、ネットワークアドレスを表すための様々な型(TCPAddr
, UDPAddr
, IPAddr
など)が存在します。これらのアドレス型は、それぞれ異なるネットワークプロトコルやアドレス形式に対応しています。
netaddr
インターフェースは、これらの異なるアドレス型を抽象化し、共通の振る舞いを定義するために使用されます。コミット前のipsock.go
ファイルを見ると、netaddr
インターフェースは以下のように定義されています。
type netaddr interface {
Network() string // "ip", "ip4", or "ip6"
String() string
toAddr() Addr
}
このインターフェースは、ネットワークアドレスが持つべき基本的なメソッド(ネットワークタイプ、文字列表現、そしてnet.Addr
インターフェースへの変換)を規定しています。toAddr()
メソッドは、具体的なアドレス型をnet.Addr
インターフェース型に変換するために使用されます。
4. net.Addr
インターフェース
net.Addr
インターフェースは、Goのnet
パッケージにおける最も基本的なネットワークアドレスの抽象化です。
type Addr interface {
Network() string // name of the network (for example, "tcp", "udp")
String() string // string form of address (for example, "192.0.2.1:25", "[2001:db8::1]:80")
}
このインターフェースは、ネットワークアドレスがどのネットワークタイプ(例: "tcp", "udp")に属し、そのアドレスの文字列表現を返すためのメソッドを定義しています。netaddr
インターフェースのtoAddr()
メソッドは、このnet.Addr
インターフェース型を返すことで、より汎用的なアドレス表現を提供します。
技術的詳細
このコミットは、net
パッケージ内のipsock.go
ファイルにaddrList
という新しい型を導入します。この型は、netaddr
インターフェースを実装する要素のスライス([]netaddr
)として定義されています。
// An addrList represents a list of network endpoint addresses.
type addrList []netaddr
addrList
型は、単なるnetaddr
のスライスであるだけでなく、netaddr
インターフェース自体も実装しています。これは、addrList
がtoAddr()
メソッドを持っているためです。
func (al addrList) toAddr() Addr {
switch len(al) {
case 0:
return nil
case 1:
return al[0].toAddr()
default:
// For now, we'll roughly pick first one without
// considering dealing with any preferences such as
// DNS TTL, transport path quality, network routing
// information.
return al[0].toAddr()
}
}
このtoAddr()
メソッドの実装は、addrList
がどのように単一のnet.Addr
に変換されるかを示しています。
- リストが空の場合:
nil
を返します。 - リストに1つの要素がある場合: その要素(
netaddr
)のtoAddr()
メソッドを呼び出し、その結果を返します。 - リストに複数の要素がある場合: 現時点では、リストの最初の要素の
toAddr()
メソッドを呼び出し、その結果を返します。コメントにあるように、これは将来的にDNS TTL、トランスポートパスの品質、ネットワークルーティング情報などの考慮事項に基づいて、より賢い選択ロジックが追加される可能性を示唆しています。しかし、このコミットの時点では、単に最初の要素を選択するだけです。
このaddrList
の導入により、Goのネットワークスタックは、複数のIPアドレスをまとめて扱い、それらをnetaddr
インターフェースとして統一的に処理できるようになります。これは、Happy Eyeballsのようなアルゴリズムを実装する上で、複数の候補アドレスを管理するための基本的なデータ構造を提供します。
コアとなるコードの変更箇所
変更はsrc/pkg/net/ipsock.go
ファイルに集中しています。
--- a/src/pkg/net/ipsock.go
+++ b/src/pkg/net/ipsock.go
@@ -40,6 +40,24 @@ type netaddr interface {
toAddr() Addr
}
+// An addrList represents a list of network endpoint addresses.
+type addrList []netaddr
+
+func (al addrList) toAddr() Addr {
+ switch len(al) {
+ case 0:
+ return nil
+ case 1:
+ return al[0].toAddr()
+ default:
+ // For now, we'll roughly pick first one without
+ // considering dealing with any preferences such as
+ // DNS TTL, transport path quality, network routing
+ // information.
+ return al[0].toAddr()
+ }
+}
+
var errNoSuitableAddress = errors.New("no suitable address found")
// firstFavoriteAddr returns an address that implemets netaddr
コアとなるコードの解説
追加されたコードは以下の通りです。
-
addrList
型の定義:// An addrList represents a list of network endpoint addresses. type addrList []netaddr
これは、
netaddr
インターフェースを実装する要素のスライスとしてaddrList
型を定義しています。これにより、複数のネットワークアドレスを一つのリストとして扱うことが可能になります。 -
addrList
のtoAddr()
メソッドの実装:func (al addrList) toAddr() Addr { switch len(al) { case 0: return nil case 1: return al[0].toAddr() default: // For now, we'll roughly pick first one without // considering dealing with any preferences such as // DNS TTL, transport path quality, network routing // information. return al[0].toAddr() } }
このメソッドは、
addrList
がnetaddr
インターフェースを満たすために必要です。netaddr
インターフェースは、toAddr() Addr
メソッドを持つことを要求します。- リストが空の場合は
nil
を返します。 - リストに1つのアドレスしかない場合は、そのアドレスの
toAddr()
メソッドを呼び出して返します。 - リストに複数のアドレスがある場合、現時点では単純にリストの最初の要素の
toAddr()
メソッドを呼び出して返します。これは、このコミットがHappy Eyeballsの「準備」段階であることを示しており、将来的に複数のアドレスの中から最適なものを選択するロジックが追加される余地を残しています。
- リストが空の場合は
この変更により、net
パッケージは複数のアドレス候補を扱うための基本的なデータ構造を手に入れ、今後のHappy Eyeballsの実装に向けた重要な一歩を踏み出しました。
関連リンク
- RFC 6555 - Happy Eyeballs: Successive Connections with Bounded Delays: https://datatracker.ietf.org/doc/html/rfc6555
- Go Issue #3610: net: IPv6 support for Dial: https://github.com/golang/go/issues/3610
- Go Issue #5267: net: Happy Eyeballs (RFC 6555) for Dial: https://github.com/golang/go/issues/5267
- Go CL 13241046: https://golang.org/cl/13241046 (これはコミットメッセージに記載されているGoのChange Listへのリンクです)
参考にした情報源リンク
- RFC 6555 - Happy Eyeballs: Successive Connections with Bounded Delays (IETF公式ドキュメント)
- Go GitHub Issues: #3610, #5267 (Goプロジェクトの公式Issueトラッカー)
- Go Change List 13241046 (Goプロジェクトの公式コードレビューシステム)
- Go言語の
net
パッケージのドキュメントとソースコード (Go公式ドキュメント) - 一般的なネットワークプログラミングとデュアルスタックに関する知識