Server-Sent Eventsの実装

Server-Sent Events(SSE)はサーバからのプッシュ配信を実現する機能。WebSocketとの違いはWebSocketがサーバからクライアント、クライアントからサーバへの双方向でのやりとりが可能であることに対して、SSEはサーバからクライアントへの片方向通信のみ対応している点。機器によるモニタリングで、状態に変化が生じた場合にリアルタイムでウェブブラウザに情報を表示させる場合に使える。

<?php
header( "Content-Type: text/event-stream" );
while ( true ) {
	$curDate = date( DATE_ISO8601 );
	echo 'data: This is a message at time ' . $curDate, "\n\n";
	while (ob_get_level() > 0) {
		ob_end_flush();
	}
	flush();
	if ( connection_aborted() ) break;
	sleep(1);
}
<html>
<head>
<meta charset='UTF-8'>
<title>Server-sent events demo</title>
</head>
<button>Close the connection</button>
<ul></ul>
<script>
	var button = document.querySelector( 'button' );
	var eventList = document.querySelector( 'ul' );
	var evtSource = new EventSource( 'sse.php' );
	evtSource.onopen = function () {
		console.log( 'Connection to server opened.' );
	};
	evtSource.onmessage = function ( e ) {
		var newElement = document.createElement( 'li' );
		newElement.textContent = 'message: ' + e.data;
		eventList.appendChild( newElement );
	};
	evtSource.onerror = function () {
		console.log( 'EventSource failed.' );
	};
	button.onclick = function () {
		console.log( 'Connection closed' );
		evtSource.close();
	};
</script>
</body>
</html>

いつもどおりapacheを使うと期待通りに動いてくれない。apacheがバッファリングしているような挙動になる(Qiitaで質問したけど今のところ回答がつかない)。

調べても情報を得られず、nginxでのバッファリング回避方法がたくさんヒットするのでこちらを試してみることにした。

# dnf install nginx php-fpm

php-fpmはapacheをアンインストールすると一緒に削除されたので再度インストール。

php-fpmの設定ファイル(/etc/php-fpm.d/www.conf)でuserとgroupを編集。

user = nginx
group = nginx

nginxの設定ファイル(/etc/nginx/nginx.conf)でバッファリングオフの記述(serverセクションに追記)。

gzip off;
proxy_buffering off;
fastcgi_keep_conn on;
fastcgi_max_temp_file_size 0;
fastcgi_buffering off;

試したところなんとも簡単に動いた。apacheのバッファリング回避はどうすれば良いんだろう。

How do I enable PHP’s flush() with nginx+PHP-FPM?

強制的にバッファを埋める方法で解決できることがわかった。

How to configure apache to work with SSE?

根本的な解決策ではないけど、他に方法がなければ仕方ないかな。

How to disable buffering with apache2 and mod_proxy_fcgi?

「ProxyIOBufferSize」「ProxyReceiveBufferSize」かも。phpがFastCGIになったことでその影響が出たのでは。でも設定方法がよくわからない。httpd.confに書いてみたけどSSEスクリプトを正常に動作しなかった。