【実務・中級編】 TCP輻輳制御アルゴリズム(Slow Start, Congestion Avoidance) – ネットワーク基礎とWebセキュリティ実践ガイド

なぜWeb APIが突然遅くなるのか?―TCP輻輳制御が支配する「目に見えない渋滞」の正体

現場でインフラを触っていると、「特定の時間帯だけAPIのレスポンスが極端に悪くなる」といった相談をよく受ける。アプリケーションコードには問題がない。ログを見てもバックエンドの負荷は低い。そんな時、多くのエンジニアが盲点とするのが、TCP層における「輻輳制御(Congestion Control)」だ。

今日は、パケットがネットワークという名の高速道路を走る際、いかにして「渋滞」を察知し、自らを律しているのか。その深淵なる世界を紐解こう。

—

1. 輻輳ウィンドウ(CWND)という名の「アクセル」

TCP通信において、送信側が相手からの確認応答(ACK)を待たずに送り出せるデータ量を決めるのが CWND(Congestion Window)だ。これはまさに、ネットワークという道路状況に合わせて踏み込む「アクセルの深さ」に相当する。

この CWND が小さすぎれば帯域を使い切れないし、大きすぎればネットワークがパンクしてパケットロスが発生する。このバランスを動的に調整するのが Slow Start と Congestion Avoidance という二大アルゴリズムだ。

スロースタート(Slow Start):控えめな発進

コネクション開始時、送信側は「この道はどれくらい空いているのか?」を知らない。そこで、まずは CWND を小さく(例えば MSS の10倍程度)設定し、ACKが返ってくるたびに CWND を倍々ゲームで増やしていく。これがスロースタートだ。

輻輳回避(Congestion Avoidance):慎重な巡航

ある一定の閾値(ssthresh:Slow Start Threshold)に達すると、急激な増大はリスクと判断し、CWND を「1 RTT(Round Trip Time)ごとに1 MSSずつ」という緩やかな増加に切り替える。これが輻輳回避フェーズである。

—

2. パケットの挙動を追跡する

現場でトラブルシューティングする際、我々は tcpdump や wireshark を使うが、まずは手元の curl でその片鱗を見てみよう。

# -v オプションでTCPハンドシェイクから通信終了までを詳細表示
# --trace-time でタイムスタンプを付与してRTTの感覚を掴む
curl -v https://api.example.com --trace-time

このとき、もし HTTP/2 や HTTP/3 を使っているなら、CWND の影響はさらに顕著だ。特に QUIC(HTTP/3)はユーザー空間で輻輳制御を行うため、OSのカーネルパラメータに依存せず、より攻撃的なスロースタートが可能になっている。

—

3. Pythonで体感する輻輳制御のロジック

理屈だけでは実感が湧かないだろう。簡易的に CWND の増減ロジックをPythonでシミュレートしてみる。

# 輻輳制御の概念シミュレーション
cwnd = 1.0       # 初期CWND
ssthresh = 64.0  # 閾値
packet_loss = False

def simulate_tcp_flow(round_trips):
    global cwnd, ssthresh
    for i in range(round_trips):
        if cwnd < ssthresh:
            # スロースタート: 指数関数的に増加
            cwnd *= 2
            print(f"RTT {i}: スロースタート中, CWND={cwnd}")
        else:
            # 輻輳回避: 線形的に増加
            cwnd += 1
            print(f"RTT {i}: 輻輳回避中, CWND={cwnd}")
        
        # パケットロスが発生したと仮定
        if i == 5:
            print("--- パケットロス検知! ---")
            ssthresh = cwnd / 2
            cwnd = 1.0 # 再びスロースタートへ
            break

simulate_tcp_flow(10)

このコードが示す通り、ひとたびロスが発生すれば、苦労して積み上げた CWND は一気にリセットされる。これが「APIの応答が急に詰まる」原因の正体だ。

—

4. 現場で役立つチューニングの心得

もし君が運用しているサーバーでパケットロスが頻発しているなら、まずはカーネルパラメータを確認してほしい。

# Linuxカーネルの輻輳制御アルゴリズムを確認
sysctl net.ipv4.tcp_congestion_control

# 現代の推奨は 'bbr' (Google開発)
# 損失ベースではなく帯域遅延積をベースに制御するため、ロスに強い
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr

bbr(Bottleneck Bandwidth and Round-trip propagation time)は、従来の Cubic などとは異なり、パケットロスを「混雑」と誤認せず、純粋なスループットを最大化するように設計されている。グローバルに展開するAPIサーバーであれば、この切り替えだけでレスポンス速度が改善するケースは極めて多い。

—

最後に:ネットワークを「意識」するエンジニアへ

「APIが遅い」と言われたとき、多くの開発者はデータベースのクエリやアプリの計算処理ばかりを疑う。しかし、凄腕のエンジニアは、パケットが通り抜ける物理的・論理的な経路、そしてTCPが自律的に制御するその「呼吸」にまで意識を向ける。

ネットワークは生き物だ。その挙動を深く理解し、適切なパラメータでチューニングを施すこと。それが、ユーザーに「速い」と感じさせるための、我々インフラ屋の矜持である。

さあ、次は tcpdump を片手に、パケットの往来を眺めてみるとしよう。きっと、昨日までとは違う景色が見えるはずだ。

コメント

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