ゲームラグ白書 › L8 ソケットとプロトコル
SO_REUSEPORTの振り分けの偏り SO_REUSEPORT imbalance, stuck worker
原因ID sk-reuseport · 主担当 ゲーム開発チーム・サーバー開発 · 副担当 インフラチーム・サーバーインフラ
図と実験のあるメインページでこのカードを開く →
同じポートを複数のプロセスで分担して受け付けると、カーネルは接続ごとにアドレスのハッシュで担当プロセスを決め、その後は変更しません。担当プロセスが1つ止まると、そこに割り当てられた人だけが待たされます。
なぜ ゲートウェイ・ログインサーバーがSO_REUSEPORTで複数のプロセスを起動 → すると 1つのプロセスがGCや過負荷で止まっても、そこに割り当てられた新しい接続とUDPパケットはほかのプロセスに回されない → 画面では 一部の人だけ接続不可・フリーズ。プロセス数が変わる再起動時には、一部のUDPセッションが切れる
- 症状
- 接続不可・無限ロード, フリーズ, 切断
- 要因
- ストール, パケットロス
- 誰に起きるか
- 自分だけ, サーバー全体
- いつ
- 接続直後・メンテ明け, ときどきランダムに
- 担当
- 主担当 ゲーム開発チーム・サーバー開発 · 副担当 インフラチーム・サーバーインフラ
- ゲーム開発チームの対応
- 受信スレッドは絶対に止まらないようにする、再起動時にセッションを引き継ぐ手順を実装。
- インフラチームの対応
- プロセスごとの接続待ちキューの監視(ssのRecv-Q)、デプロイでプロセス数を変えるときはセッション引き継ぎの手順に従うようにする。
- グラフでは
- 一部だけ高い · リッスンソケットごとの接続待ちキュー(Recv-Q)
- 確認箇所
- ss -ltnpで、同じポートのリッスンソケットごとのRecv-Q(acceptを待っている接続数)と担当プロセスを確認し、プロセスごとのスループットを比較
- 該当する場合
- 同じポートの複数のソケットのうち1つだけRecv-Qがたまり続け、そのプロセスが止まっているかスループットが0に近い
- 該当しない場合
- すべてのソケットのRecv-Qが均等にたまっていれば、全体の過負荷(「接続待ちキュー(backlog)のあふれ」)
- 確認手段
- インフラのツールで確認(ゲームコード不要)
出典
- socket(7) — Linux manual page Linux man-pages
SO_REUSEPORTで複数のソケットが同じアドレスにbindし、TCP接続とUDPパケットを分担して受け取る - net/core/sock_reuseport.c (Linux v6.12) Linux kernel
BPFプログラムがなければ、パケットのハッシュ値をグループのソケット数で割って担当ソケットを選ぶ - Why does one NGINX worker take all the load? Cloudflare
SO_REUSEPORTは単純なハッシュでワーカーごとにキューを分けるため、ワーカーが1つ詰まると、そのキューにたまった接続がすべて止まる - net/ipv4/tcp_diag.c (Linux v6.12) Linux kernel
ssのRecv-Q・Send-Qの値:リッスンソケットではacceptを待っている接続数とbacklogの上限、接続済みソケットではアプリがまだ読んでいないバイト数とACKを受け取っていない送信バイト数
あわせて読みたい原因
同じ層:L8 ソケットとプロトコル
同じ症状(接続不可・無限ロード)を起こすほかの層の原因
図と実験のあるメインページでこのカードを見る