[インデックス 17437] ファイルの概要
このコミットは、Go言語の net
パッケージに netaddr
インターフェースを追加するものです。この新しいインターフェースは、単一のネットワークエンドポイントアドレス、またはIPアドレスの短いリストを運ぶことを目的としており、今後のコミットで導入されるダイヤルヘルパー関数で使用される予定です。これは、RFC 6555で記述されているデュアルIPスタックノードでの高速フェイルオーバーを伴うTCP接続設定の準備として行われました。
コミット
commit 3c6558ad904debb65b554baaacb1cb23ea7839d4
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Fri Aug 30 09:09:45 2013 +0900
net: add netaddr interface
This CL adds the netaddr interface that will carry a single network
endpoint address or 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/13368044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3c6558ad904debb65b554baaacb1cb23ea7839d4
元コミット内容
net: add netaddr interface
この変更は、今後のCL(Change List)でダイヤルヘルパー関数に単一のネットワークエンドポイントアドレスまたはIPアドレスの短いリストを渡すための netaddr
インターフェースを追加します。
これは、RFC 6555で説明されているデュアルIPスタックノードでの高速フェイルオーバーを伴うTCP接続設定の準備です。
変更の背景
このコミットの主な背景は、デュアルスタック(IPv4とIPv6の両方をサポートする)環境におけるネットワーク接続のパフォーマンスと信頼性を向上させることです。特に、RFC 6555「Happy Eyeballs: Success with Dual-Stack Hosts」で提唱されている「高速フェイルオーバー」メカニズムをGoのネットワークスタックに組み込むための準備として、この netaddr
インターフェースが導入されました。
従来のデュアルスタック環境では、クライアントアプリケーションがIPv6アドレスとIPv4アドレスの両方を持つサーバーに接続しようとした際、もしIPv6接続が失敗したり遅延したりすると、IPv4へのフォールバックまでに数秒のタイムアウトが発生し、ユーザー体験が著しく損なわれるという問題がありました。RFC 6555は、この遅延を緩和するために、IPv4とIPv6の両方で接続試行を並行して(またはわずかな時間差で)開始し、最初に成功した接続を使用するというアプローチを提案しています。これにより、ユーザーは遅延を感じることなく、利用可能な最速の接続を利用できるようになります。
このコミットは、この「Happy Eyeballs」アルゴリズムをGoの net
パッケージに実装するための基盤となる変更であり、ネットワークエンドポイントアドレスをより柔軟に扱うための抽象化レイヤーとして netaddr
インターフェースを導入しています。
前提知識の解説
- デュアルスタック (Dual-Stack): ネットワークデバイスやホストがIPv4とIPv6の両方のプロトコルスタックを同時にサポートし、両方のアドレスタイプで通信できる状態を指します。
- IPv4とIPv6:
- IPv4 (Internet Protocol version 4): 現在広く使用されているインターネットプロトコルのバージョンで、32ビットのアドレスを使用します。アドレス枯渇の問題が指摘されています。
- IPv6 (Internet Protocol version 6): IPv4の後継として設計されたインターネットプロトコルのバージョンで、128ビットのアドレスを使用し、より広大なアドレス空間を提供します。
- ネットワークエンドポイントアドレス: ネットワーク上の特定のサービスやアプリケーションを識別するための情報で、通常はIPアドレスとポート番号の組み合わせで構成されます。例えば、
192.168.1.1:8080
や[::1]:443
など。 - RFC 6555 (Happy Eyeballs): 「Happy Eyeballs: Success with Dual-Stack Hosts」というタイトルのIETF標準ドキュメントです。デュアルスタック環境における接続遅延の問題を解決するために、IPv4とIPv6の接続試行を並行して行うことで、ユーザー体験を向上させるメカニズムを定義しています。
- アルゴリズムの概要:
- クライアントがホスト名を解決し、IPv4とIPv6の両方のアドレスを取得します。
- 通常、優先されるアドレスファミリー(例: IPv6)のアドレスへの接続試行を開始します。
- もし最初の接続試行が短いタイムアウト(ブラウザでは通常300ミリ秒程度)内に完了しない場合、クライアントはもう一方のアドレスファミリー(例: IPv4)のアドレスを使用して、並行して2番目の接続試行を開始します。
- 最初に確立された接続が使用され、他の保留中の接続試行はキャンセルまたは破棄されます。
- このアプローチにより、ユーザーは最速の接続を体験でき、目に見える遅延なしに動作するIPスタックに「フェイルオーバー」することができます。
- アルゴリズムの概要:
- 高速フェイルオーバー: ネットワーク接続において、障害が発生した場合やパフォーマンスが低下した場合に、別の利用可能な経路やプロトコルに迅速に切り替えるメカニズムを指します。Happy Eyeballsは、デュアルスタック環境における高速フェイルオーバーの一種です。
技術的詳細
このコミットの核心は、netaddr
という新しいインターフェースの導入と、既存の Addr
型(IPAddr
, TCPAddr
, UDPAddr
, UnixAddr
)がこのインターフェースを実装するように変更された点です。
netaddr
インターフェースの導入
src/pkg/net/ipsock.go
に以下のインターフェースが追加されました。
// A netaddr represents a network endpoint address or a list of
// network endpoint addresses.
type netaddr interface {
// toAddr returns the address represented in Addr interface.
// It returns a nil interface when the address is nil.
toAddr() Addr
}
このインターフェースは、ネットワークエンドポイントアドレス、またはアドレスのリストを抽象化することを目的としています。特に重要なのは toAddr()
メソッドで、これは netaddr
インターフェースを実装する型が、Goの標準的なネットワークアドレスを表す net.Addr
インターフェースのインスタンスを返すことを保証します。これにより、異なる種類のアドレス(IP、TCP、UDP、Unixなど)を統一的に扱うことが可能になります。
既存の Addr
型への toAddr()
メソッドの追加
IPAddr
, TCPAddr
, UDPAddr
, UnixAddr
といった既存の具体的なアドレス型に、netaddr
インターフェースを満たすための toAddr()
メソッドが追加されました。例えば、IPAddr
の場合:
func (a *IPAddr) toAddr() Addr {
if a == nil {
return nil
}
return a
}
このメソッドは、レシーバである IPAddr
自体を net.Addr
型として返します。これは、これらのアドレス型がすでに net.Addr
インターフェースを満たしているため、単純な型アサーションで実現できます。nil
チェックは、レシーバが nil
の場合に nil
インターフェースを返すための安全策です。
firstFavoriteAddr
関数の変更
src/pkg/net/ipsock.go
内の firstFavoriteAddr
関数は、以前は IP
型を返していましたが、netaddr
インターフェースを返すように変更されました。また、inetaddr
という新しい引数が追加され、これは IP
から netaddr
を生成する関数です。
変更前:
func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP)
func firstSupportedAddr(filter func(IP) IP, addrs []string) IP
変更後:
func firstFavoriteAddr(filter func(IP) IP, addrs []string, inetaddr func(IP) netaddr) (netaddr, error)
func firstSupportedAddr(filter func(IP) IP, addrs []string, inetaddr func(IP) netaddr) (netaddr, error)
この変更により、firstFavoriteAddr
は単なるIPアドレスだけでなく、より具体的なネットワークエンドポイントアドレス(TCPAddr, UDPAddrなど)を netaddr
インターフェースとして返すことができるようになりました。これにより、Happy Eyeballsのような複数のアドレスタイプを扱うロジックが、より汎用的に実装できるようになります。
resolveInternetAddr
関数の変更
resolveInternetAddr
関数も、戻り値の型が Addr
から netaddr
に変更されました。また、内部で inetaddr
というクロージャを定義し、これを使って IP
から適切な netaddr
型を生成するように変更されています。
変更前:
func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error)
変更後:
func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error)
この変更により、resolveInternetAddr
は解決されたアドレスを netaddr
インターフェースとして返すようになり、呼び出し元は toAddr()
メソッドを通じて具体的な Addr
型にアクセスできます。
dial.go
および dial_gen.go
の変更
resolveAddr
関数が Addr
ではなく netaddr
を返すように変更され、それに伴い dial
関数への引数も ra.toAddr()
を介して Addr
型に変換されて渡されるようになりました。
// dial.go
-func resolveAddr(op, net, addr string, deadline time.Time) (Addr, error) {
+func resolveAddr(op, net, addr string, deadline time.Time) (netaddr, error) {
// dial_gen.go, fd_unix.go, fd_windows.go
- return dial(net, addr, localAddr, ra, noDeadline)
+ return dial(net, addr, localAddr, ra.toAddr(), noDeadline)
これらの変更は、netaddr
インターフェースがネットワークアドレスの抽象化レイヤーとして機能し、今後の高速フェイルオーバーの実装において、より柔軟なアドレス選択と接続試行を可能にするための基盤を構築していることを示しています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に以下のファイルに集中しています。
src/pkg/net/ipsock.go
:netaddr
インターフェースの定義。firstFavoriteAddr
およびfirstSupportedAddr
関数のシグネチャ変更と、netaddr
を返すように内部ロジックの修正。resolveInternetAddr
関数のシグネチャ変更と、netaddr
を返すように内部ロジックの修正。errNoSuitableAddress
エラーの追加。
src/pkg/net/iprawsock.go
,src/pkg/net/tcpsock.go
,src/pkg/net/udpsock.go
,src/pkg/net/unixsock.go
:- それぞれの
Addr
型(IPAddr
,TCPAddr
,UDPAddr
,UnixAddr
)にtoAddr() Addr
メソッドを追加し、netaddr
インターフェースを実装するように変更。
- それぞれの
src/pkg/net/sock_posix.go
:sockaddr
インターフェースがnetaddr
インターフェースを埋め込むように変更。sockaddr
インターフェースからtoAddr() sockaddr
メソッドを削除(netaddr
インターフェースのtoAddr() Addr
に置き換えられたため)。
src/pkg/net/dial.go
,src/pkg/net/dial_gen.go
,src/pkg/net/fd_unix.go
,src/pkg/net/fd_windows.go
:resolveAddr
関数の戻り値の型をAddr
からnetaddr
に変更。dial
関数を呼び出す際に、netaddr
型の変数に対してtoAddr()
メソッドを呼び出してAddr
型に変換してから渡すように変更。
コアとなるコードの解説
netaddr
インターフェース
// src/pkg/net/ipsock.go
type netaddr interface {
// toAddr returns the address represented in Addr interface.
// It returns a nil interface when the address is nil.
toAddr() Addr
}
このインターフェースは、ネットワークアドレスの抽象化を提供します。toAddr()
メソッドは、netaddr
を実装する任意の型が、Goの標準的なネットワークアドレスインターフェースである net.Addr
に変換可能であることを保証します。これにより、異なる種類のアドレス(IP、TCP、UDP、Unixなど)を統一的に扱うことができ、特にHappy Eyeballsのような複数のアドレスタイプを考慮するロジックの実装が容易になります。
Addr
型への toAddr()
メソッドの実装例 (IPAddr
)
// src/pkg/net/iprawsock.go
func (a *IPAddr) toAddr() Addr {
if a == nil {
return nil
}
return a
}
IPAddr
、TCPAddr
、UDPAddr
、UnixAddr
の各型にこの toAddr()
メソッドが追加されました。これらの型は元々 net.Addr
インターフェースを満たしているため、メソッドの実装はシンプルにレシーバ自身を Addr
型として返すだけです。nil
チェックは、nil
レシーバに対してメソッドが呼び出された場合にパニックを防ぎ、nil
インターフェースを返すためのものです。
firstFavoriteAddr
関数の変更
// src/pkg/net/ipsock.go
func firstFavoriteAddr(filter func(IP) IP, addrs []string, inetaddr func(IP) netaddr) (netaddr, error) {
// ... (既存のロジック) ...
if filter == nil {
addr, err := firstSupportedAddr(ipv4only, addrs, inetaddr)
if err != nil {
addr, err = firstSupportedAddr(anyaddr, addrs, inetaddr)
}
return addr, err
} else {
return firstSupportedAddr(filter, addrs, inetaddr)
}
}
func firstSupportedAddr(filter func(IP) IP, addrs []string, inetaddr func(IP) netaddr) (netaddr, error) {
for _, s := range addrs {
if ip := filter(ParseIP(s)); ip != nil {
return inetaddr(ip), nil // ここでinetaddrを使ってnetaddrを生成
}
}
return nil, errNoSuitableAddress
}
firstFavoriteAddr
と firstSupportedAddr
は、与えられたIPアドレスのリストから、指定されたフィルター条件に合致する最初のアドレスを見つける関数です。変更点として、inetaddr func(IP) netaddr
という新しい引数が追加されました。これは、見つかった IP
アドレスを、具体的な netaddr
型(例: TCPAddr
や UDPAddr
)に変換するためのファクトリ関数として機能します。これにより、これらの関数は単なるIPアドレスではなく、より完全なネットワークエンドポイントアドレスを netaddr
インターフェースとして返すことができるようになりました。
resolveInternetAddr
関数の変更
// src/pkg/net/ipsock.go
func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error) {
// ... (既存のロジック) ...
inetaddr := func(ip IP) netaddr {
switch net {
case "tcp", "tcp4", "tcp6":
return &TCPAddr{IP: ip, Port: portnum, Zone: zone}
case "udp", "udp4", "udp6":
return &UDPAddr{IP: ip, Port: portnum, Zone: zone}
case "ip", "ip4", "ip6":
return &IPAddr{IP: ip, Zone: zone}
default:
panic("unexpected network: " + net)
}
}
// ... (IPアドレスのパースとDNSルックアップのロジック) ...
return firstFavoriteAddr(filter, addrs, inetaddr) // inetaddrを渡してnetaddrを取得
}
resolveInternetAddr
は、ホスト名とポート番号からインターネットアドレスを解決する関数です。この関数内で inetaddr
というクロージャが定義され、これは引数として受け取った IP
アドレスと、解決されたポート番号やゾーン情報に基づいて、適切な TCPAddr
、UDPAddr
、または IPAddr
のインスタンスを netaddr
型として返します。最終的に、firstFavoriteAddr
を呼び出す際にこの inetaddr
クロージャを渡すことで、解決されたIPアドレスから具体的な netaddr
型のインスタンスを取得し、それを呼び出し元に返します。
これらの変更は、Goのネットワークスタックが、Happy Eyeballsのような高度な接続ロジックをサポートするために、アドレスの表現と処理をより柔軟かつ抽象的に行うための重要なステップです。
関連リンク
- Go CL 13368044: https://golang.org/cl/13368044
- Go Issue #3610: https://golang.org/issue/3610
- Go Issue #5267: https://golang.org/issue/5267
- RFC 6555 - Happy Eyeballs: Success with Dual-Stack Hosts: https://datatracker.ietf.org/doc/html/rfc6555
参考にした情報源リンク
- RFC 6555, officially titled "Happy Eyeballs: Success with Dual-Stack Hosts," is an Internet Engineering Task Force (IETF) standard that addresses the issue of connection delays experienced by dual-stack (IPv4 and IPv6) client applications.
- The core concept of Happy Eyeballs is to mitigate this delay by attempting to establish connections over both IPv4 and IPv6 in a staggered manner, and then using the connection that succeeds first.
- RFC 6555 has been widely adopted and implemented in most major web browsers and many other applications to improve network performance in dual-stack environments. It has since been obsoleted by RFC 8305, "Happy Eyeballs Version 2: Better Connectivity Using Concurrency," which refines the algorithm and provides more detailed guidance on aspects such as DNS query handling, managing multiple addresses within each family, and leveraging historical connection information.
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF6YnG-Mb5oE0UZYaFiEv7X2pyUA1K-DifkilOpV4lzQgco1JOU7VTotlaaxxDaErrusLH16V2ThKhJ2rkIokl-_RiKmXtogdrL_cdV5vFeusdor5-757s7BQ3lECVPBodpIx6yV6V_O5yJYbzbIY03P_K0MRtFmZAvrlSJRGTxM1GMCVrU01jn5YDl4CrCopA=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFHhW_Z3a8hwWWgnjcKSGNcCARsKyFIa7sa-Y21rrkEgzt-yzPVVIihvTnquS-pY0SdLd-y-GoxRBugxIaGDKksLNPczL6UiBvW_XTQqs0xF2cOIh-vfiGEm8XpAhYTL1w_gxIPAA==