web-dev-qa-db-ja.com

サーバー送信イベントは実際にどのように機能しますか?

したがって、サーバー送信イベント(EventSource)の概念を理解しています。

  • クライアントはEventSourceを介してエンドポイントに接続します
  • クライアントはエンドポイントから送信されたメッセージを聞くだけです

私が混乱しているのは、サーバー上でどのように機能するかです。私はさまざまな例を見てきましたが、頭に浮かぶのはMozillaの例です: http://hacks.mozilla.org/2011/06/a-wall-powered-by-eventsource-and- server-sent-events /

これは悪い例かもしれませんが、私が理解しているように、サーバー側がどのように機能するかは少し理にかなっています。

  • データベースなどのデータストアで何かが変更された
  • サーバーサイドスクリプトは、N秒ごとにデータストアをポーリングします
  • ポーリングスクリプトが変更に気付いた場合、サーバー送信イベントがクライアントに発生します

それは理にかなっていますか?それは本当にベアボーンの観点からどのように機能するのでしょうか?

43
Ahmed Nuaman

HTML5ドクターサイトには、サーバー送信イベントに関する すばらしい記事 がありますが、ここでも(合理的に)短い要約を提供しようと思います。

サーバー送信イベントは、基本的に、長時間実行されるhttp接続、特別なmimeタイプ(text/event-stream)、およびEventSourceAPIを提供するユーザーエージェントです。これらが一緒になって、サーバーとクライアント間の単方向接続の基盤を作り、メッセージを送信できますサーバーからクライアントへ

サーバー側では、それはかなり単純です。本当に行う必要があるのは、次のhttpヘッダーを設定することだけです。

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

200やその他のコードではなくコード204で応答するようにしてください。これにより、準拠しているユーザーエージェントが切断されます。また、サーバー側で接続を終了しないように注意してください。これで、その接続にメッセージをプッシュし始めることができます。 nodejs(expressを使用)では、これは次のようになります。

app.get("/my-stream", function(req, res) {
    res.status(200)
       .set({ "content-type"  : "text/event-stream"
            , "cache-control" : "no-cache"
            , "connection"    : "keep-alive"
            })

    res.write("data: Hello, world!\n\n")
})

クライアントでは、次のようにEventSourceAPIを使用するだけです。

var source = new EventSource("/my-stream")
source.addEventListener("message", function(message) {
    console.log(message.data)
})

基本的にはこれで終わりです。

さて、実際には、ここで実際に起こることは、相互契約によってサーバーとクライアントによって接続が維持されることです。サーバーは、適切と思われる限り、接続を維持します。必要に応じて、接続を終了し、次にクライアントが接続を試みたときに204 No Contentで応答する場合があります。これにより、クライアントは再接続の試行を停止します。クライアントがまったく再接続しないように指示され、それによってクライアントが一度再接続しようとするのをスキップする方法で接続を終了する方法があるかどうかはわかりません。

前述のように、クライアントは接続も維持し、切断された場合は再接続を試みます。再接続するアルゴリズムは spec で指定されており、かなり単純です。

しかし、これまでほとんど触れていない非常に重要なビットの1つは、mimeタイプです。 mimeタイプは、接続を介して送信されるメッセージの形式を定義します。ただし、メッセージのcontentsの形式を指示するのではなく、メッセージ自体の構造を指示するだけであることに注意してください。 mimeタイプは非常に単純です。メッセージは基本的に、情報のキーと値のペアです。キーは、事前定義されたセットの1つである必要があります。

  • id-メッセージのID
  • データ-実際のデータ
  • イベント-イベントタイプ
  • 再試行-失敗した接続を再試行する前にユーザーエージェントが待機する必要があるミリ秒

その他のキーは無視してください。次に、メッセージは2つの改行文字を使用して区切られます。\n\n

有効なメッセージは次のとおりです:(冗長性のために追加された最後の改行文字)

data: Hello, world!
\n

クライアントはこれをHello, world!として表示します。

これは次のとおりです。

data: Hello,
data: world!
\n

クライアントはこれをHello,\nworld!として表示します。

これは、サーバーから送信されたイベントが何であるかをほぼ要約しています。つまり、長時間実行されるキャッシュされていないhttp接続、mimeタイプ、および単純なjavascriptAPIです。

詳細については、 仕様 を読むことを強くお勧めします。これは小さく、非常によく説明されています(ただし、サーバー側の要件はもう少しうまく要約できる可能性があります)。たとえば、特定のhttpステータスコードで予想される動作について読むことを強くお勧めします。

60
Marcus Stade

また、必ず res.flushHeaders() を呼び出す必要があります。そうしないと、res.end()を呼び出すまでNode.jsはHTTPヘッダーを送信しません。完全な例については、 このチュートリアル を参照してください。

0
vkarpov15