QUICの輻輳制御:なぜ「TCPの常識」を捨ててBBRへ舵を切るのか
ネットワークエンジニアの皆さん、こんにちは。TCPの時代、私たちは「パケットロス=輻輳(Congestion)」という大前提のもと、RenoやCUBICといったアルゴリズムと何十年も付き合ってきました。しかし、HTTP/3の基盤であるQUICは、その前提を根底から覆そうとしています。
QUICはUDPベースであり、輻輳制御をカーネルではなく「ユーザー空間」で実装します。これはつまり、「アプリケーションが必要に応じて最適な輻輳制御アルゴリズムを選べるようになった」ことを意味します。今回は、現場のエンジニアが知っておくべき輻輳制御のリアルと、その選定基準について紐解いていきましょう。
—
1. 輻輳制御の「今」を知る:NewRenoからBBRまで
QUICスタック(quic-go, mvfst, lsquicなど)でよく目にするアルゴリズムには、大きく分けて「ロスベース」と「モデルベース」の2系統があります。
ロスベース(NewReno / CUBIC)
- NewReno: 古典的です。パケットロスを検出するとCWND(輻輳ウィンドウ)を半分にする。現代の高速・長距離回線では、わずかなバーストロスだけでスループットが劇的に低下するため、もはや時代遅れです。
- CUBIC: Linuxのデフォルト。ウィンドウサイズを3次関数的に増加させます。帯域利用効率は良いですが、バッファブロート(ネットワーク機器のバッファ溢れ)に弱く、常に「ロスして初めて減速する」という後手に回る挙動が課題です。
モデルベース(BBR: Bottleneck Bandwidth and Round-trip propagation time)
- BBR: Googleが開発した革命児です。ロスではなく「伝送遅延」と「帯域幅」をリアルタイムに推定し、パケットがバッファで待たされる(キューイング遅延が発生する)直前で送信レートを制御します。「ロスを待たずに、ボトルネックの最大帯域を見極める」。これが今のWeb API設計における最適解です。
—
2. 現場のデバッグ:ロス検知時の挙動
QUICは、TCPの「累積確認応答(ACK)」とは異なり、「パケット番号(Packet Number)」と「ACKフレーム」を使って、どのパケットが届き、どれが失われたかを詳細に追跡します。
例えば、`Packet 10`がロスしたと判定された場合:
1. ロス判定: `Packet 12`のACKが届いた時点で、QUICスタックは「10は届いていないが、11は届いている」と判断し、迅速に再送を行います。
2. 挙動の差:
- CUBIC: ここでCWNDを縮小し、送信ペースを落とします。
- BBR: パケットロスを「帯域不足のサイン」とは直結させません。ネットワークの品質が悪化しただけ(あるいは単なるノイズ)と判断し、モデルを維持したまま再送を行います。
—
3. 実践:QUICの挙動を検証する
まずは、自分の環境でQUIC通信がどのアルゴリズムを使っているか、あるいはどう設定するかを確認しましょう。ここではGo言語の`quic-go`を例に挙げます。
QUIC通信の設定例(Go)
import (
“github.com/quic-go/quic-go”
)
// サーバー設定の肝:輻輳制御の指定
var quicConfig = &quic.Config{
// ここでアルゴリズムを明示的に指定可能(デフォルトはBBRベースが多い)
// 運用環境に合わせて調整する
EnableDatagrams: true,
}
// 実際の接続フロー(概念コード)
func dialQUIC(addr string) {
// 接続確立時に0-RTTを試行する設定
tlsConfig := &tls.Config{ / 0-RTTの設定 / }
conn, _ := quic.DialAddr(context.Background(), addr, tlsConfig, quicConfig)
// ストリームを開いて通信
stream, _ := conn.OpenStreamSync(context.Background())
stream.Write([]byte(“Hello QUIC!”))
}
curlでQUIC(HTTP/3)の状態を確認する
デバッグの第一歩は、サーバーが正しくHTTP/3を返しているかの確認です。
-v で詳細表示、–http3 でQUICを指定
curl -v –http3 https://your-api-server.com
出力結果の中に `h3` というプロトコル名が見えれば成功です。もしパケットロス時の挙動を詳しく追いたい場合は、`Wireshark`でUDPポート(デフォルト443)をキャプチャし、`quic.frame_type == 0x02`(ACKフレーム)をフィルタリングしてください。
—
4. エンジニアへの教訓:何を基準に選ぶべきか?
実務で「どのアルゴリズムを採用すべきか」と迷ったら、以下の基準で判断してください。
1. データ転送メイン(動画配信・大容量ファイル): 迷わず BBR v2 です。帯域を限界まで使い切り、かつロスに強いため、スループットが安定します。
2. リアルタイム性重視(API通信・チャット): 依然として CUBIC も選択肢に入ります。BBRは高負荷時に若干のジッターを生む可能性があるため、極めて短いトランザクションであれば、従来のロスベースの方が予測可能な挙動を示します。
3. モバイル環境: モバイルは頻繁にハンドオーバー(基地局切り替え)が発生します。この際の瞬間的なロスに対して、BBRのモデルが過剰に反応しないようチューニングが必要です。
まとめ:現場の勘所
「なぜかHTTP/3にすると特定の拠点で遅くなる」というトラブルに遭遇したら、まずはサーバー側の輻輳制御アルゴリズムを疑ってください。CUBICからBBRへ切り替えるだけで、パケットロス率が数%あるような劣悪な回線でも、パフォーマンスが2倍〜3倍に跳ね上がることは珍しくありません。
ネットワークは生き物です。RFCの仕様書を暗記するだけでなく、実環境で `tcpdump` を取り、パケットのシーケンスを眺める。その泥臭い作業こそが、真のインフラエンジニアへの近道です。
次の記事では、QUICにおける「0-RTT」が引き起こすセキュリティリスク(リプレイ攻撃)と、その防衛策について深く掘り下げていきます。お楽しみに。
コメント