yagisukeのWebなブログ

フロントエンドとサーバーサイドをさまようエンジニア

第3話: Server-Sent Eventsの雑多なメモ 〜 REAL WORLD HTTP第7章より 〜

yagisuke.hatenadiary.com の第3話です。

Server-Sent Events(SSE)

SSEはHTML5の機能のひとつです。 巨大なファイルコンテンツを「少しずつ送信」するHTTP/1.1のChunked形式の通信機能を応用し、 サーバーから任意のタイミングでクライアントにイベント通知できる機能です。

2014年にはGREEさんがすでにバックエンドに採用していました。
http://labs.gree.jp/blog/2014/08/11070/

サーバーから情報を送る方法には、Comet(4章参照)がありました。 クライアントから定期的にリクエストを送ることでサーバー側のイベントを検出する(ポーリング)、 あるいはリクエストを受け取った状態で返事を保留する(ロングポーリング)方法がよく使われていました。

ロングポーリングのフロー f:id:yagi_suke:20180126010540p:plain

画像: http://kimulla.hatenablog.com/entry/2016/01/17/リアルタイムなwebアプリを実現する方法%28ポーリン

Cometのロングポーリング + Chunked レスポンス を組み合わせて、 1度のリクエストに対して、サーバーから複数のイベント送信を実現したのがSSEです。

イベントストリームについて

SSEはChunked形式を使っていますが、HTTPの上に別のテキストプロトコルを載せています。 これはイベントストリームと呼ばれ、MIMEタイプはtext/event-streamです。

イベントストリームの例

id: 10
event: ping
data: { "time": 2017-09-25T01:27:00+000 }

id: 11
data: Message from pySpa
data: #eng channel
タグの種類 説明
id イベントを識別するID。再送処理で使用
event イベント名を設定
data イベントと共におくられるデータ(jsonがよく使われる)
retry 再接続の待ち時間のパラメータ(ミリ秒)

SSEのサンプルソース

// SSE APIは、EventSourceインターフェイスに含まれています。
// イベントを受け取るためにサーバへの接続を開始には、
// イベントを生成するスクリプトのURIを指定する、新たなEventSourceオブジェクトを作成します。
// インスタンスを生成したら、メッセージの受け取りを始めることができます。
const evtSource = new EventSource('ssedemo.php')

// このコードはeventフィールドを持たない、サーバからの通知を受信して、
// メッセージのテキストをドキュメントのHTMLにあるリストへ追加します。
evtSource.onmessage = function(event) {
  const newElement = document.createElement('li')

  newElement.innerHTML = `message: ${event.data}`
  eventList.appendChild(newElement)
}

// addEventListener() を使用して特定のイベントを待ち受けることもできます
// 前のコードと似ていますが、eventフィールドに"ping"が設定されたメッセージが
// サーバから送られたときに、自動的に呼び出されることが異なります。
// こちらはdataフィールドのJSONをパースして、情報を出力します。
evtSource.addEventListener('ping', function(event) {
  const newElement = document.createElement('li')
  const obj = JSON.parse(event.data)

  newElement.innerHTML = `ping at ${obj.time}`
  eventList.appendChild(newElement);
}, false);

ソース元: https://developer.mozilla.org/ja/docs/Server-sent_events/Using_server-sent_events

注意 - IEが対応していません。polyfillで対応可能。 https://developer.mozilla.org/ja/docs/Server-sent_events/Using_server-sent_events