[インデックス 11822] ファイルの概要
このコミットは、Go言語のネットワークパッケージにおいて、BSD系のOSにおけるSO_REUSEPORT
ソケットオプションの「野放図な使用(wild use)」を無効化することを目的としています。具体的には、src/pkg/net/sockopt_bsd.go
ファイル内で、リスナーソケットに対するSO_REUSEPORT
の設定を削除し、マルチキャストソケットにのみ限定することで、以前に修正されたはずの問題(Issue #2830)が再発するのを防いでいます。
コミット
commit 6fa2296e839d7cea090fd9bddc9831ea6186c30e
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Mon Feb 13 12:45:59 2012 +0900
net: disable wild use of SO_REUSEPORT on BSD variants
Fixes #2830 (again).
R=rsc
CC=golang-dev
https://golang.org/cl/5651083
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6fa2296e839d7cea090fd9bddc9831ea6186c30e
元コミット内容
net: disable wild use of SO_REUSEPORT on BSD variants
Fixes #2830 (again).
R=rsc
CC=golang-dev
https://golang.org/cl/5651083
変更の背景
このコミットの背景には、Go言語のネットワークパッケージがBSD系のオペレーティングシステム(macOS、FreeBSDなど)でSO_REUSEPORT
ソケットオプションをどのように扱うかという問題があります。コミットメッセージにある「Fixes #2830 (again)」という記述から、この問題が過去にも発生し、一度修正されたにもかかわらず再発したことが示唆されます。
SO_REUSEPORT
は、複数のソケットが同じIPアドレスとポートにバインドすることを許可するオプションです。これは、特にマルチキャストアプリケーションや、サーバーのダウンタイムなしでの再起動(graceful restart)を可能にするために有用です。しかし、その動作はOSによって異なり、特にBSD系とLinux系では振る舞いが異なります。
Goの標準ライブラリが、BSD系OSでリスナーソケットに対して無条件にSO_REUSEPORT
を設定していたことが、何らかの予期せぬ問題を引き起こしていたと考えられます。この「野放図な使用」とは、おそらく、特定のユースケース(マルチキャストなど)に限定されるべきSO_REUSEPORT
が、一般的なTCPリスナーなどにも適用されていたことを指しているでしょう。これにより、ポートの競合や接続の予期せぬルーティングなど、安定性や予測可能性に影響を与える問題が発生した可能性があります。
このコミットは、SO_REUSEPORT
の適用範囲をマルチキャストソケットに限定することで、この問題を根本的に解決しようとしています。
前提知識の解説
ソケットオプション SO_REUSEADDR
と SO_REUSEPORT
ネットワークプログラミングにおいて、ソケットオプションはソケットの振る舞いを制御するために使用されます。特に、SO_REUSEADDR
とSO_REUSEPORT
は、ポートの再利用に関連する重要なオプションです。
-
SO_REUSEADDR
:- このオプションは、ソケットが
TIME_WAIT
状態にあるアドレスにバインドすることを許可します。 - TCP接続が終了すると、ソケットはしばらくの間
TIME_WAIT
状態にとどまります。これは、ネットワーク上の遅延パケットが新しい接続に影響を与えないようにするためです。 SO_REUSEADDR
を設定することで、サーバーがクラッシュしたり、再起動したりした際に、以前の接続がTIME_WAIT
状態であってもすぐに同じポートにバインドできるようになります。これにより、「Address already in use」エラーを防ぎ、サーバーの迅速な再起動を可能にします。- Go言語の
net
パッケージでは、TCPListener
に対してデフォルトでSO_REUSEADDR
が設定されます。
- このオプションは、ソケットが
-
SO_REUSEPORT
:- このオプションは、複数の異なるソケット(異なるプロセスによって所有されている場合も含む)が、全く同じIPアドレスとポートの組み合わせに同時にバインドすることを許可します。
- 主な用途は、複数のリスナープロセス間で受信接続をロードバランシングすることです。カーネルが受信接続を、同じポートにバインドしている複数のリスニングソケットのいずれかに分散します。
- OSによる動作の違い:
- BSD系(FreeBSD, macOSなど): 伝統的に、
SO_REUSEPORT
は、複数のプロセスが同じポートにバインドすることを許可し、特にUDP/IPマルチキャストやブロードキャストデータグラムを受信するために使用されます。TCPソケットの場合、BSDのSO_REUSEPORT
は、接続を最も新しくバインドされたソケットにキューイングするLIFO(後入れ先出し)のような動作をすることがありました。これにより、新しいプロセスがポートにバインドして新しい接続を引き継ぎ、古いプロセスが既存の接続を優雅に処理してからシャットダウンすることで、シームレスなサーバー再起動が可能になります。FreeBSDでは、ロードバランシングを目的としたSO_REUSEPORT_LB
も導入されています。 - Linux: Linuxカーネル3.9以降で導入された
SO_REUSEPORT
は、主にロードバランサーとして機能し、複数のリスナー間で受信接続を分散します。これは、マルチスレッドサーバーアプリケーションの「thundering herd problem」(多数のプロセスが同時に同じイベントを待機し、イベントが発生した際にすべてが起動して競合する問題)を軽減するのに役立ちます。
- BSD系(FreeBSD, macOSなど): 伝統的に、
このコミットは、特にBSD系OSにおけるSO_REUSEPORT
の挙動と、それがGoのネットワークスタックに与える影響に焦点を当てています。
技術的詳細
このコミットは、Goのnet
パッケージがBSD系のOSでSO_REUSEPORT
をどのように設定するかを変更しています。変更前のコードでは、setDefaultListenerSockopts
関数内で、リスナーソケットに対して無条件にSO_REUSEPORT
が設定されていました。コメントには「最近使用されたポートの再利用を許可する」とあり、その目的が「効果的なマルチキャストアプリケーションと迅速な描画を必要とするアプリケーションを可能にするため」と説明されています。
しかし、この無条件な設定が問題を引き起こしていました。SO_REUSEPORT
は強力なオプションであり、その動作はOSによって微妙に異なります。特にBSD系OSでは、複数のソケットが同じポートにバインドできるため、意図しないポートの競合や、接続がどのリスナーにルーティングされるかの予測不能性につながる可能性がありました。一般的なTCPリスナーに対してこのオプションが常に有効になっていると、アプリケーションの設計によっては予期せぬ振る舞いを引き起こすことがあります。
コミットメッセージの「Fixes #2830 (again)」は、この問題が以前にも報告され、修正が試みられたものの、何らかの理由で再発したことを示しています。おそらく、以前の修正では根本的な原因が解決されていなかったか、あるいは別の変更によって問題が再燃したのでしょう。
このコミットの解決策は、SO_REUSEPORT
の設定をより限定的にすることです。具体的には、setDefaultListenerSockopts
(一般的なリスナーソケット用)からSO_REUSEPORT
の設定を削除し、setDefaultMulticastSockopts
(マルチキャストソケット用)にのみ残すことで、このオプションの適用範囲を、その本来の目的であるマルチキャストアプリケーションに限定しています。これにより、一般的なTCPリスナーがSO_REUSEPORT
の予期せぬ影響を受けることを防ぎ、ネットワークスタックの安定性と予測可能性を向上させています。
コアとなるコードの変更箇所
diff --git a/src/pkg/net/sockopt_bsd.go b/src/pkg/net/sockopt_bsd.go
index 519d2fb05a..79e0e57e21 100644
--- a/src/pkg/net/sockopt_bsd.go
+++ b/src/pkg/net/sockopt_bsd.go
@@ -34,14 +34,6 @@ func setDefaultListenerSockopts(s int) error {
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
- // Allow reuse of recently-used ports.
- // This option is supported only in descendants of 4.4BSD,
- // to make an effective multicast application and an application
- // that requires quick draw possible.
- err = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
return nil
}
@@ -52,6 +44,10 @@ func setDefaultMulticastSockopts(s int) error {
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
+ // Allow reuse of recently-used ports.
+ // This option is supported only in descendants of 4.4BSD,
+ // to make an effective multicast application that requires
+ // quick draw possible.
err = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1)
if err != nil {
return os.NewSyscallError("setsockopt", err)
コアとなるコードの解説
このコミットは、src/pkg/net/sockopt_bsd.go
ファイル内の2つの関数に変更を加えています。
-
setDefaultListenerSockopts(s int) error
:- この関数は、一般的なリスナーソケット(例:
net.Listen
で作成されるTCPリスナー)に対してデフォルトのソケットオプションを設定するために呼び出されます。 - 変更点: 以前は、この関数内で
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1)
が呼び出され、SO_REUSEPORT
オプションが無条件に設定されていました。このコミットでは、この8行のコードブロックが完全に削除されています。 - 影響: これにより、BSD系のOSにおいて、Goの一般的なリスナーソケットはデフォルトで
SO_REUSEPORT
オプションを設定しなくなります。これにより、複数のプロセスが同じポートにバインドしようとした際の予期せぬ挙動や競合が回避され、より予測可能なソケットの振る舞いが保証されます。
- この関数は、一般的なリスナーソケット(例:
-
setDefaultMulticastSockopts(s int) error
:- この関数は、マルチキャスト通信に使用されるソケットに対してデフォルトのソケットオプションを設定するために呼び出されます。
- 変更点: 以前のコードでは、この関数内でも
SO_REUSEPORT
が設定されていましたが、その前のコメントが短縮されていました。このコミットでは、SO_REUSEPORT
を設定する行自体は残しつつ、その前のコメントがsetDefaultListenerSockopts
から削除されたコメントと同じ内容(// Allow reuse of recently-used ports. ...
)に更新されています。 - 影響: マルチキャストソケットに対しては引き続き
SO_REUSEPORT
が設定されます。これは、マルチキャストアプリケーションが同じポートで複数のソケットをリッスンする必要があるというSO_REUSEPORT
の本来の目的と合致しています。コメントの更新は、このオプションがマルチキャストの文脈でなぜ重要であるかを明確にするためのものです。
要するに、この変更はSO_REUSEPORT
の適用範囲を、一般的なリスナーソケットからマルチキャストソケットに限定することで、BSD系OSにおけるGoのネットワークスタックの堅牢性と予測可能性を高めています。
関連リンク
- Go Issue #2830: https://github.com/golang/go/issues/2830 (このコミットが修正した問題のトラッカー)
- Go Code Review: https://golang.org/cl/5651083 (この変更のコードレビューページ)
参考にした情報源リンク
SO_REUSEPORT
on BSD behavior: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGkhf1Gtb8WuCT8mObJfTSw7QH4YKsd54ECtnemkWOm3-kL8M9Z2U673FaQnwilUm-u1gd11ZXb73LijYPWs6hZzy4vGXYjsLs_vWHJHKr6jkEQNFhQFhr8JzzlGE3NZdEHRlBvDIa7b4MpsHeR5qCxx31gefESnL-u14TUaNrmq05mSO_REUSEPORT
vsSO_REUSEADDR
in Go: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGQdRnIZUkBWi3EU9JjavQuyYHw1ct1mDxljpOjcXbxKR92P0zNWMqm1T9HuSpb2XH5PGVspQxLWwj3QJ0DwzqgBO7ZdPwN8Xnc6uI1DnFF6BtWE8iH5tEEpg-AEu0kMGUs7FEu_Tr0gAcO0OPVlYKtXMGRmL3OdnDQMAK_8ruAJMiGGURCHPvy14GQOGrCMntawJ5K1VPh- Linux
SO_REUSEPORT
man page: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF6-168e03pP9R-yapauAkKevnoP33DWaCgjxIh7LDBnaonJIlxgIplNfUfFByJo5te6PIY_2yVu4KD4CyBIh6bcck6KPMsUqy_nqEMz4gkISssZsFg-dQuOi-EqSCqy45Srj1KBarLKD_ZemXyWw==