QUICを活用したスマートフォンアプリでのRTT測定手法
海外の特定環境のモバイル通信網の状況と特定クラウドサービスの通信品質について、いくつかの指標でデータを取得や予想をしていきたいです。
ネットワーク通信環境を測定する1つの指標として RTT(Round Trip Time)測定 があります。
世の中にはいろいろな遅延測定サービスがありますが、モバイル網を使った測定ができるものが少ないと感じており、それを実測したいと考えたところから派生した技術挑戦です。モバイルでの実測値と固定網を利用した世の中のサービスの間の関係性が見えてくれば、そういったデータを活用した事前予測を立てやすくなると考えています。
従来の測定手法では、スマートフォンアプリの制約により実現が困難なケースが増えています。本記事では、QUIC プロトコル を活用したRTT測定アプローチを試したので共有します。
従来のRTT測定手法とその限界
1. ICMP Echo Request/Reply(ping)
# 従来の測定方法
ping -c 4 example.com
メリット:
- シンプルで直感的
- OSレベルでの正確な測定
スマートフォンでの制約:
- アプリからのICMP送信は基本的に不可
- 管理者権限が必要
- プラットフォーム(iOS/Android)の制限
2. TCP_INFO による測定
// Linux でのTCP_INFO取得例
struct tcp_info info;
socklen_t info_len = sizeof(info);
getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, &info_len);
printf("RTT: %u microseconds\n", info.tcpi_rtt);
メリット:
- カーネルレベルでの情報
- アプリケーション層での取得可能
スマートフォンでの制約:
- プラットフォーム固有のAPI制限
- サンドボックス環境での権限不足
- 継続的な測定の困難さ
3. 静的コンテンツダウンロードによる擬似測定
// 小さな静的ファイルのダウンロード時間から推測
async function measureRTTViaDownload() {
const startTime = performance.now();
// 1KBの小さなファイルをダウンロード
const response = await fetch('https://cdn.example.com/1kb.txt?' + Date.now());
const endTime = performance.now();
const totalTime = endTime - startTime;
// RTTは往復時間の概算(ダウンロード時間の一部として推測)
const estimatedRTT = totalTime / 2; // 簡易的な推測
return estimatedRTT;
}
メリット:
- 実装が簡単
- 特別な権限が不要
- クロスプラットフォーム対応
課題:
- CDNキャッシュの影響: キャッシュヒット時は数msで完了するため、ネットワーク遅延が測定できない
- ファイルサイズの影響: サイズが大きいとダウンロード時間が支配的になり、RTTが埋もれる
- サーバー処理時間: 純粋なネットワーク遅延以外の要素が混入
- 測定精度: 実際のRTTとは大きく乖離する可能性
QUICを用いたアプローチ
QUICプロトコルにおけるRTT測定の仕組み
QUIC(Quick UDP Internet Connections)は、その仕様内に RTT推定機能 を内蔵しています。これは輻輳制御やフロー制御のために設計された仕組みです。
参考仕様:
- RFC 9000 - QUIC: A UDP-Based Multiplexed and Secure Transport (IETF, May 2021)
- RFC 9002 - QUIC Loss Detection and Congestion Control (IETF, May 2021)
RTT測定の原理
Client Server
| |
|--- Initial Packet --| t1
| |
|-- ACK with timestamp | t2
| |
RTT = t2 - t1
QUIC RTT測定の技術的詳細
1. パケットレベルでの測定
QUICは各パケットにタイムスタンプを付与し、ACK(確認応答)で往復時間を計算:
// Web APIでのQUIC RTT取得例(概念的)
const connection = new QuicConnection('https://cdn.example.com');
const rttInfo = await connection.getConnectionStats();
console.log(`Current RTT: ${rttInfo.smoothedRTT}ms`);
2. Smoothed RTT計算
QUICは SRTT(Smoothed Round Trip Time) を以下の式で算出:
SRTT = (1-α) * SRTT + α * RTT_sample
RTTVar = (1-β) * RTTVar + β * |SRTT - RTT_sample|
仕様詳細:
- RFC 9002 Section 5.3 では、latest_rtt、smoothed_rtt、rttvar、min_rtt等のRTT測定変数を定義
- 時間ベースの損失検出にはkTimeThreshold(RTT倍数)を使用
CDNとの組み合わせによる実用性
現代のCDN環境
昨今、主要CDNプロバイダーがQUICプロトコルとHTTP/3への対応を積極的に進めています。Cloudflareは早期からQUICサポートを開始し、全世界のエッジロケーションでHTTP/3を提供しています。Amazon CloudFrontもAWSエコシステムとの統合を活かしてQUIC対応を実現し、Google Cloud CDNはYouTubeをはじめとする大規模動画配信での実績を基にQUIC最適化を行っています。Fastlyも高性能エッジコンピューティング機能と組み合わせたQUIC実装を提供しており、これらのCDN環境ではQUICベースのRTT測定が実用的に活用できる状況が整っています。
CDN以外についても測定対象としてありえますが、CDNのほうがより自由度が低いと言うことで今回の記事ではメインのトピックとして取り上げます。
CDN-端末間RTT測定の優位性
1. キャッシュヒット率に依存しない測定
従来のHTTP測定:
RTT = ネットワーク遅延 + サーバー処理時間 + キャッシュ状態
QUIC RTT測定:
RTT = 純粋なネットワーク遅延(制御フレームベース)
2. 継続的モニタリング
// 継続的RTT監視の実装例
class QuicRTTMonitor {
constructor(cdnEndpoint) {
this.endpoint = cdnEndpoint;
this.rttHistory = [];
}
async startMonitoring() {
setInterval(async () => {
const rtt = await this.measureRTT();
this.rttHistory.push({
timestamp: Date.now(),
rtt: rtt,
endpoint: this.endpoint
});
}, 5000); // 5秒間隔
}
async measureRTT() {
// QUIC接続からRTT情報を取得
const stats = await this.connection.getStats();
return stats.smoothedRTT;
}
}
実装における考慮事項
1. ブラウザAPI活用
現代のブラウザでは Performance API や WebTransport を通じてQUIC統計にアクセス可能:
関連仕様:
- W3C Performance Timeline - Performance API仕様
- WebTransport over HTTP/3 (IETF Draft) - WebTransport仕様
// Performance API経由でのRTT取得
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.nextHopProtocol === 'h3') {
console.log(`QUIC RTT: ${entry.connectEnd - entry.connectStart}ms`);
}
});
});
observer.observe({entryTypes: ['navigation', 'resource']});
2. ngtcp2を使用したネイティブ実装
ngtcp2ライブラリを活用したQUIC RTT測定では、ブラウザAPIでは取得困難な詳細統計にアクセス可能:
参考:
- ngtcp2 GitHub Repository - 軽量QUIC実装
- ngtcp2 API Documentation - API仕様
// ngtcp2を使用したRTT・輻輳制御統計取得
ngtcp2_conn_stat stat;
ngtcp2_conn_get_conn_stat(conn, &stat);
printf("RTT統計:\n");
printf(" Smoothed RTT: %lu μs\n", stat.smoothed_rtt);
printf(" RTT Variance: %lu μs\n", stat.rttvar);
printf(" Min RTT: %lu μs\n", stat.min_rtt);
printf("輻輳制御統計:\n");
printf(" CWND: %lu bytes\n", stat.cwnd);
printf(" Bytes in Flight: %lu bytes\n", stat.bytes_in_flight);
printf(" Pacing Rate: %lu bytes/sec\n", stat.pacing_rate);
取得可能な主要メトリクス
メトリクス | 説明 | ブラウザAPI | ngtcp2 |
---|---|---|---|
Smoothed RTT | 平滑化RTT | △ 推測 | ✅ 直接 |
RTT Variance | RTT分散 | ❌ | ✅ |
CWND | 輻輳ウィンドウ | ❌ | ✅ |
Bytes in Flight | 送信中バイト数 | ❌ | ✅ |
Pacing Rate | ペーシング速度 | ❌ | ✅ |
Packet Loss | パケット損失 | ❌ | ✅ |
まとめ
QUICプロトコルを活用したRTT測定は、従来手法の制約を克服するアプローチとして有意義かと感じます。
いろいろ応用をして実験から学びがありますが、それはまた別の機会に。
Discussion