はじめに
趣味で、Raspberry Pi と Raspberry Pi Pico W を使って、LAN 内で完結する IoT アプリケーションを作っています。
Raspberry Pi Pico W が LAN に接続したタイミングで、クライアントに通知をする機能を作っていたのですが、その際に SSE (Server-Sent Events) を使いました。
クライアントから Application サーバーに対して直接繋いでいた際には、遅延もほとんどなくリアルタイムに通知が表示されていたのですが、
nginx をリバースプロキシとして噛ませたところ、リアルタイムで表示されずに、遅延が発生するようになりました。
調べてみたところ、nginx をリバースプロキシに SSE を使ってリアルタイムに通知をしたい場合には、追加で設定が必要そうだったので、それについてまとめます。
遅延の原因
nginx をリバースプロキシとして噛ませたことによって、遅延が発生するようになったのは、nginx のバッファリングが原因でした。
When buffering is enabled, nginx receives a response from the proxied server as soon as possible, saving it into the buffers set by the proxy_buffer_size and proxy_buffers directives. If the whole response does not fit into memory, a part of it can be saved to a temporary file on the disk. Writing to temporary files is controlled by the proxy_max_temp_file_size and proxy_temp_file_write_size directives.
When buffering is disabled, the response is passed to a client synchronously, immediately as it is received. nginx will not try to read the whole response from the proxied server. The maximum size of the data that nginx can receive from the server at a time is set by the proxy_buffer_size directive.
nginx は、proxy している Application サーバーからのレスポンスをバッファリングしてから、クライアントにレスポンスを返すようにすることができるのですが、この機能は、デフォルトで on になっています。
なので、Application サーバーからのレスポンスが完了する、もしくは指定されたサイズのデータがバッファーに溜まらない限りはクライアントにレスポンスを返さないようになっています。
SSE では、接続を維持した状態で、細切れにされたデータを送信するようになっているため、この条件だと、Application サーバーからのレスポンスデータがバッファの閾値を超えるまで nginx の内部に留まってしまいます。
対応方法
nginx の設定ファイルを変更する
nginx 側の設定で、proxy_buffering を off に設定します。
server や http の context で設定してしまうと、パフォーマンスの観点から、本来バッファリングが必要な箇所でもバッファリングされなくなってしまうため、影響範囲を小さくするために location の context で設定するようにします。
location /api/events/ {
proxy_buffering off;
# ...
}
Application 側で X-Accel-Buffering ヘッダーを設定するようにする
Buffering can also be enabled or disabled by passing “yes” or “no” in the “X-Accel-Buffering” response header field. This capability can be disabled using the proxy_ignore_headers directive.
とあるように、Application サーバーからのレスポンスのヘッダーに、X-Accel-Buffering を no で設定することでも対応することができます。
参考