ロングポーリング
サーバーがデータの準備ができるまでレスポンスを保留し、擬似的なリアルタイム通信を実現する手法
Webリアルタイム
ロングポーリングとは
ロングポーリング (Long Polling) は、クライアントがサーバーにリクエストを送り、サーバーがデータの準備ができるまでレスポンスを保留する手法である。通常のポーリング (短いポーリング) が定期的にリクエストを繰り返すのに対し、ロングポーリングはサーバー側でイベントが発生するまで接続を維持する。
通常のポーリングとの比較
短いポーリング:
クライアント → リクエスト → サーバー (データなし → 即座に空レスポンス)
クライアント → リクエスト → サーバー (データなし → 即座に空レスポンス)
クライアント → リクエスト → サーバー (データあり → レスポンス)
ロングポーリング:
クライアント → リクエスト → サーバー (データが来るまで保留...)
(データあり → レスポンス)
クライアント → 即座に再リクエスト → サーバー (保留...)
リアルタイム通信手法の比較
| 手法 | 方向 | レイテンシ | 複雑さ | サーバー負荷 |
|---|---|---|---|---|
| 短いポーリング | クライアント → サーバー | 高い (間隔依存) | 低い | 高い (空リクエスト多数) |
| ロングポーリング | 擬似双方向 | 中程度 | 中程度 | 中程度 |
| SSE | サーバー → クライアント | 低い | 低い | 低い |
| WebSocket | 双方向 | 最低 | 高い | 低い |
クライアント側の実装
async function longPoll(url: string) {
while (true) {
try {
const response = await fetch(url, {
signal: AbortSignal.timeout(30000), // 30秒タイムアウト
});
const data = await response.json();
handleData(data);
} catch (error) {
if (error instanceof DOMException && error.name === 'TimeoutError') {
continue; // タイムアウト → 再接続
}
await new Promise(r => setTimeout(r, 3000)); // エラー → 3秒待って再接続
}
}
}
サーバー側の実装
app.get('/api/notifications', async (req, res) => {
const timeout = setTimeout(() => {
res.json({ data: null }); // 30秒でタイムアウト
}, 30000);
const data = await waitForNewData(req.query.lastId); // データが来るまで待機
clearTimeout(timeout);
res.json({ data });
});
Lambda での制約
Lambda は最大 29 秒 (API Gateway のタイムアウト) しか接続を維持できないため、ロングポーリングには不向き。代替手段として、API Gateway WebSocket (Lambda と WebSocket の統合)、AppSync Subscriptions (GraphQL のリアルタイム通知)、SQS + 短いポーリング (クライアントが SQS を定期的にチェック) がある。
使うべきケース
- WebSocket や SSE が使えない環境 (プロキシ制限、古いブラウザ)
- シンプルな通知機能で、WebSocket ほどの双方向通信が不要
- 既存の HTTP インフラをそのまま使いたい場合
ロングポーリングの理解を深めるには関連書籍が参考になる。