ロングポーリング

サーバーがデータの準備ができるまでレスポンスを保留し、擬似的なリアルタイム通信を実現する手法

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 インフラをそのまま使いたい場合

ロングポーリングの理解を深めるには関連書籍が参考になる。

関連用語