【実務・中級編】 TCPの再送制御アルゴリズム(RTO算出と指数バックオフ) – ネットワーク基礎とWebセキュリティ実践ガイド

ネットワークの「沈黙」を読み解く:TCP再送制御とRTOが教えてくれる現場の真実

「APIのレスポンスがたまに異常に遅い」「特定の環境下でパケットロスが頻発する」。
現場でこんな相談を受けたとき、多くのエンジニアはまずアプリケーションログやDBのクエリを見に行きます。しかし、そこで何も見つからないとき、我々ネットワークエンジニアの出番です。

パケットは嘘をつきません。TCPが「沈黙」したとき、裏側では一体何が起きているのか。今回は、TCPの信頼性を支える心臓部、再送制御(RTO算出と指数バックオフ)の深淵を覗いてみましょう。

—

RTO(再送タイムアウト)という名の「待ち時間」の正体

TCPは、パケットを送ったあと、相手から ACK(受信確認)が返ってくるのを待ちます。この「いつまで待つか」を定義するのが RTO(Retransmission Timeout)です。

もし RTO が短すぎれば、まだ届いているかもしれないパケットを「紛失した」と誤認して再送してしまい、無駄なトラフィックがネットワークを圧迫します。逆に長すぎれば、本当のパケットロスが発生した際に復旧が遅れ、ユーザー体験が著しく低下します。

RTOは「生き物」である

RTOは固定値ではありません。RFC 6298で定義されている通り、往復遅延時間(RTT)の観測値に基づいて動的に計算されます。

1. SRTT (Smoothed RTT): 過去のRTTを平滑化した値。
2. RTTVAR (RTT Variation): RTTの揺らぎ(分散)。

現場で重要なのは、ネットワークの負荷が高まるとこの RTTVAR が大きくなり、結果として RTO が伸びるという点です。つまり、「不安定な回線ほど、再送の判断が慎重になる」という適応能力をTCPは持っているのです。

—

輻輳と戦う「指数バックオフ」の泥臭い哲学

パケットが届かない理由が「単なる回線のノイズ」なら再送は有効ですが、もし「ルーターのバッファ溢れ(輻輳)」が原因だったらどうでしょう? そこで再送を繰り返すと、ネットワークはさらに混雑し、状況は悪化の一途をたどります。

ここで登場するのが指数バックオフです。

再送が発生するたびに、TCPは RTO の値を 2倍、4倍、8倍……と指数関数的に増やしていきます。これは「今はネットワークが悲鳴を上げているから、少し静かにしていよう」という、TCPの非常に人間味あふれる(そして賢明な)自制心なのです。

—

実践:Linuxでのチューニングとデバッグ

現場では、このデフォルトの挙動がアプリケーションの要求スペックと合わないことがあります。例えば、超低遅延が求められるリアルタイム通信では、再送までの待ち時間をあえて短く調整することもあります。

1. 現在のカーネル設定を確認する

Linuxサーバーで再送の最大回数は net.ipv4.tcp_retries2 で制御されています。

# 現在の再送回数制限を確認(デフォルトは通常15)
sysctl net.ipv4.tcp_retries2

# もし再送制限を厳しくして、早くエラーを検知させたい場合は以下のように設定
# ただし、ネットワークが不安定な環境では注意が必要
sudo sysctl -w net.ipv4.tcp_retries2=5

2. Pythonで再送挙動をシミュレートする(概念コード)

アプリケーション側でタイムアウトを制御する際、socket モジュールの TCP_USER_TIMEOUT を使うと、カーネルの再送ロジックを待たずにアプリケーションレベルで制御を打ち切ることができます。

import socket

# ソケットを作成
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# TCP_USER_TIMEOUTを設定 (単位はミリ秒)
# ネットワーク経由のデータ送信がこの時間を超えたら強制的に切断する
# これにより、指数バックオフで数分間固まるのを防ぐ
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 3000)

print("TCP_USER_TIMEOUTを3秒に設定しました。")

—

トラブルシューティングのTips:パケットキャプチャで何を見るか

ネットワークが遅いと感じたら、tcpdump で再送を確認しましょう。以下のコマンドは、再送が発生したパケットだけを抽出する鉄板のコマンドです。

# 再送パケット(フラグ:[S] ではない再送)をキャプチャする
sudo tcpdump -ni eth0 'tcp[tcpflags] & (tcp-push|tcp-ack) != 0 and tcp.analysis.retransmission'

ここで Retransmission が頻発しているなら、以下の3点を確認してください。

  • MTUの不一致: ICMP の「Destination Unreachable」が遮断されていないか。
  • 輻輳: ネットワーク機器のインタフェースで discard カウンタが増えていないか。
  • セッションの飽和: Ephemeral Port が枯渇して、接続再利用ができず再送扱いになっていないか。

—

最後に:エンジニアとして持つべき視点

TCPの再送制御は、単なるプロトコルの仕様ではありません。それは、「不確実な世界で、いかにして確実なデータを届けるか」という先人たちの苦闘の結晶です。

APIのタイムアウト設定を決めるときも、単に「なんとなく5秒」とするのではなく、「この通信経路のRTTを考慮すると、再送が一度発生して復旧するまでに何秒かかるか?」を想像してみてください。

ネットワークの挙動を理解したエンジニアの書くコードは、どんな荒波のようなトラフィックにも耐えうる強靭さを持っています。ぜひ、次回のデバッグでは Wireshark や tcpdump の先にあるパケットの呼吸を感じ取ってみてください。

それでは、また次回の深掘りでお会いしましょう。現場からは以上です。

コメント

タイトルとURLをコピーしました