LaravelでSSE(Server Sent Events)を利用してサーバから通知する

laravel-sse

皆さん、SSEServer Sent Events)をご存知でしょうか?
Node.jsが有名過ぎて隠れてしまっていますが、双方向通信が必要なく、サーバーからクライアント一方向通信するなら、SSEの方が断然簡単です!
今回はそんなSSELaravelに導入したいと思います。

SSE導入の目的

例えば、外部APIを利用し、データをリアルタイムに取得して表示したい場合に利用します。
下記の例では、単純な数字をサーバーからクライアントへ送信するサンプルです。

SSEのメリット

HTTPで通信するので通信の互換性が高く、別のソケットを確保したり、サーバー側で特別な設定をする必要がありませんので、レンタルサーバーでも簡単に構築することができ、JavaScriptの知識だけで済むので学習コストが掛かりません。

サンプルコード

Laravelフレームワークを利用する際、SSEをどこに記述しようか、かなりなやみますが、サンプルではとりあえずコントローラにしておきます。

コントローラ(サーバ側)

SSEレスポンスは、LaravelがベースにしているSymfonyフレームワークStreamedResponseを使用してクライアント側に返します。

    public function sse(Request $request)
    {
        $response = new StreamedResponse(function() use ($request) {
           $requestData = $request->all();
           while(true) {
                $datas = $requestData;
                echo 'data: ' . json_encode($datas) . "nn";
                ob_flush();
                flush();
                sleep(1);
            }
        });
        $response->headers->set('Content-Type', 'text/event-stream');
        $response->headers->set('X-Accel-Buffering', 'no');
        $response->headers->set('Cache-Control', 'no-cache');
        return $response;
    }

コントローラ解説

StreamedResponseのコールバック関数に引数を渡したい場合は、useを使用してください。
ここでは、クライアント側から送信されてくるリクエストパラメータをそのまま渡しています。
while(true)サーバー側で処理を繰り返します。
$datasに何かしらのデータを配列で格納し、JSON形式でクライアント側に送信します。
sleep()関数に次の処理まで何秒待つかを記述します。
ここでは1秒毎にwhile(true) {...}内の処理を繰り返し行います。

ビュー(クライアント側)

ビューが表示された際にコントローラで設定した処理を開始するように記述します。
そして、サーバから受信したデータをどうにかこうにかします。

<script>
var datas;
var es = new EventSource("{{ route('sse', ['x' => 1, 'y' => 2]) }}");
es.addEventListener('message', function (e) {
  datas = JSON.parse(e.data);
  console.log(datas);
  // datas.x = 1;
  // datas.y = 2;
});
<script>

ビュー解説

new EventSource()サーバー側の処理が開始されます。
サンプルでは、['x' => 1, 'y' => 2]というリクエストパラメータサーバー側に渡して、同じデータを受信するようにしています。
サーバーから送られてくるJSON形式のデータをパースして、datas変数に格納しています。
ESSエラーが発生した場合は以下のように処理を停止することもできます。

es.addEventListener('error', function (e) {
  es.close();
});

擬似的にリアルタイムも可能!

勘が良い方はもうお気付きだと思いますが、擬似的に双方向のリアルタイム通信を再現することもできます。
WebRTCリアルタイムチャット)を例に簡単にご説明します。
要は、while(true) {...}の処理中にデータベースを参照し、既読フラグが立っていないメッセージを送信するだけです。
つまり、データをどこかに溜めて、双方向通信しているように思わせるわけです。
具体的なコードはまた今度にします。

SSEの可能性

個人的にSSEはもっと活躍できるのではと考えています。
SSEを利用したWebRTCは、いつか本気で実現させようと思っています。
ただ、iOSの場合のみブラウザやデスクトップのプッシュ通知機能がサポートされていないので、今か今かとずっと待っていますが…。


Amazonベストセラー

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA