😎

Node.js+Express+nginx環境下においてSSE(Server‑Sent Events)のバッファリングする問題の解決策

に公開

背景

Node.js+Express+nginx 環境下でSSE(Server‑Sent Events)を実装する際、サーバー側やミドルウェアのバッファリングにより応答が遅延してしまう問題があった。
本記事では アプリケーションコードだけで遅延を解消する方法を紹介する。

SSE(Server-Sent Events)とは?

クライアントとサーバー間で一度だけHTTP接続を確立し、その接続を維持しながらサーバーからクライアントへ一方向にイベントを送信できる技術。
クライアントからサーバーへの通信はできない(一方向通信)。

結論

先に結論。

  • レスポンスヘッダーでX-Accel-Buffering: noを送信してnginx のバッファリングを無効化する
  • compressionミドルウェア使用時は送信タイミングでres.flush()を呼ぶ

全体のコード例としては以下のようになる。

router.get('/events', function (req, res, next) {
    res.status(200).set({
        'Content-Type': 'text/event-stream;',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'X-Accel-Buffering': 'no'
    });

    res.flushHeaders();

    const text = 'これはテスト用テキストです。';
    let index = 0;
    const intervalId = setInterval(() => {
        if (index < text.length) {
            res.write(`data: ${text[index]}\n\n`);
            index++;
        } else {
            clearInterval(intervalId);
            res.end();
        }

        if (res.flush) {
           res.flush();
        }
    }, 500);

    req.on('close', () => {
        clearInterval(intervalId);
        res.end();
    });
});

原因

nginxのバッファリング

nginxはデフォルトでレスポンスを一定サイズまでバッファリングしてからクライアントへ転送するため遅延が発生する。
対策は次のいずれかとなる。

  • nginxの設定ファイルでバッファリングをオフにする
  • アプリケーション側でX-Accel-Buffering: noヘッダーを付与する

nginxの設定ファイルを変更すると、全ての箇所に影響が出るため、X-Accel-Buffering: noヘッダーを付与する方法が望ましい。

参考: https://take-engineer.com/nginx_server-sent-events/

compressionのバッファリング

compressionは圧縮のためにレスポンスをバッファリングする。
そのため、SSEでは遅延の原因になる。

対策方法としては、送信したいタイミングでres.flush()実行することでチャンクを即時送信する。

まとめ

バッファリングが疑われる場合は、まずX-Accel-Buffering: noを試す。

改善しない場合はcompressionを利用しているか確認し、利用している場合は送信したいタイミングでres.flush()を実行する。

それでもバッファリングされる場合は、ほかの箇所に原因がある可能性が高い。

Discussion