HTTP / 2を介した単方向データフローにWebSocketの代わりにSSEを使用する
公開: 2022-03-10Webアプリケーションを構築するときは、使用する配信メカニズムの種類を考慮する必要があります。 リアルタイムデータを処理するクロスプラットフォームアプリケーションがあるとしましょう。 リアルタイムで株式を売買する機能を提供する株式市場アプリケーション。 このアプリケーションは、さまざまなユーザーにさまざまな価値をもたらすウィジェットで構成されています。
サーバーからクライアントへのデータ配信に関しては、クライアントプルまたはサーバープッシュの2つの一般的なアプローチに制限されています。 Webアプリケーションの簡単な例として、クライアントはWebブラウザです。 ブラウザのWebサイトがサーバーにデータを要求している場合、これはクライアントプルと呼ばれます。 逆に、サーバーがWebサイトに更新をプロアクティブにプッシュしている場合、サーバープッシュと呼ばれます。
現在、これらを実装する方法はいくつかあります。
- ロング/ショートポーリング(クライアントプル)
- WebSocket(サーバープッシュ)
- サーバー-送信されたイベント(サーバープッシュ)。
ビジネスケースの要件を設定した後、3つの選択肢を詳しく見ていきます。
ビジネスケース
株式市場アプリケーション用の新しいウィジェットを迅速に提供し、プラットフォーム全体を再デプロイせずにプラグアンドプレイできるようにするには、ウィジェットを自己完結型にし、独自のデータI / Oを管理する必要があります。 ウィジェットは互いに結合されていません。 理想的なケースでは、それらすべてがAPIエンドポイントにサブスクライブし、そこからデータの取得を開始します。 新機能の市場投入までの時間の短縮に加えて、このアプローチにより、コンテンツをサードパーティのWebサイトにエクスポートできるようになりますが、ウィジェットは必要なものすべてを独自に提供します。
ここでの主な落とし穴は、接続の数がウィジェットの数に比例して増加し、一度に処理されるHTTPリクエストの数に対するブラウザーの制限に達することです。
ウィジェットが受け取るデータは、主に数値とその数値の更新で構成されています。最初の応答には、いくつかの市場価値を持つ10の株式が含まれています。 これには、株式の追加/削除の更新、および現在提示されている株式の市場価値の更新が含まれます。 更新ごとに少量のJSON文字列を可能な限り高速に転送します。
HTTP / 2は、同じドメインからのリクエストの多重化を提供します。つまり、複数のレスポンスに対して1つの接続しか取得できません。 これで問題が解決するようです。 まず、データを取得するためのさまざまなオプションを検討し、それらから何を取得できるかを確認します。
- NGINXを使用して負荷分散を行い、プロキシを使用してすべてのエンドポイントを同じドメインの背後に隠します。 これにより、HTTP / 2多重化をすぐに使用できるようになります。
- モバイルデバイスのネットワークとバッテリーを効率的に使用したいと考えています。
代替案
ロングポーリング
クライアントプルは、車の後部座席に座って「もうそこにいますか?」と絶えず尋ねる迷惑な子供に相当するソフトウェア実装です。 つまり、クライアントはサーバーにデータを要求します。 サーバーにはデータがなく、応答を送信する前に一定時間待機します。
- 待機中に何かがポップアップした場合、サーバーはそれを送信してリクエストを閉じます。
- 送信するものがなく、最大待機時間が達成された場合、サーバーはデータがないという応答を送信します。
- どちらの場合も、クライアントはデータの次のリクエストを開きます。
- 泡立てて、すすぎ、繰り返します。
AJAX呼び出しはHTTPプロトコルで機能します。つまり、同じドメインへのリクエストはデフォルトで多重化されます。 ただし、必要に応じてそれを機能させることで、複数の問題が発生しました。 ウィジェットアプローチで特定した落とし穴のいくつかは次のとおりです。
ヘッダーオーバーヘッド
すべてのポーリング要求と応答は完全なHTTPメッセージであり、メッセージフレーミングにHTTPヘッダーの完全なセットが含まれています。 頻繁にメッセージが少ない場合、ヘッダーは実際に送信されるデータの大部分を表します。 実際の有用なペイロードは、送信された合計バイト数よりもはるかに少なくなります(たとえば、5KBのデータに対して15KBのヘッダー)。最大レイテンシ
サーバーが応答した後、クライアントが次の要求を送信するまで、サーバーはクライアントにデータを送信できなくなります。 長いポーリングの平均遅延は1つのネットワークトランジットに近いですが、最大遅延は3つのネットワークトランジット(応答、要求、応答)を超えています。 ただし、パケット損失と再送信のため、TCP / IPプロトコルの最大遅延は3つを超えるネットワークトランジットになります(HTTPパイプラインで回避可能)。 直接LAN接続では、これは大きな問題ではありませんが、移動中やネットワークセルの切り替え中に1つになります。 ある程度、これはSSEとWebSocketで観察されますが、効果はポーリングで最大になります。接続の確立
多くのポーリング要求に再利用可能な永続的なHTTP接続を使用することで回避できますが、接続を維持するために、それに応じてすべてのコンポーネントを短時間でポーリングするようにタイミングを合わせるのは難しいです。 最終的に、サーバーの応答に応じて、ポーリングは非同期になります。パフォーマンスの低下
負荷がかかっている長いポーリングクライアント(またはサーバー)は、メッセージの遅延を犠牲にしてパフォーマンスを低下させる自然な傾向があります。 その場合、クライアントにプッシュされたイベントはキューに入れられます。 これは実際には実装によって異なります。 この場合、ウィジェットに追加/削除/更新イベントを送信するときに、すべてのデータを集約する必要があります。タイムアウト
サーバーがクライアントに送信するものを取得するまで、長いポーリング要求は保留状態のままにする必要があります。 これにより、プロキシサーバーが長時間アイドル状態のままになると、接続がプロキシサーバーによって閉じられる可能性があります。多重化
これは、永続的なHTTP / 2接続を介して応答が同時に発生する場合に発生する可能性があります。 ポーリング応答は実際には同期できないため、これを行うのは難しい場合があります。
長いポーリングで発生する可能性のある実際の問題の詳細については、こちらをご覧ください。
WebSocket
サーバープッシュメソッドの最初の例として、WebSocketを見ていきます。
MDN経由:
WebSocketsは、ユーザーのブラウザーとサーバーの間で対話型の通信セッションを開くことを可能にする高度なテクノロジーです。 このAPIを使用すると、サーバーにメッセージを送信し、サーバーをポーリングして応答することなく、イベント駆動型の応答を受信できます。
これは、単一のTCP接続を介して全二重通信チャネルを提供する通信プロトコルです。
HTTPとWebSocketはどちらも、OSIモデルのアプリケーション層に配置されているため、層4のTCPに依存しています。
- 申し込み
- プレゼンテーション
- セッション
- 輸送
- 通信網
- データリンク
- 物理的
RFC 6455は、WebSocketが「HTTPポート80および443で動作し、HTTPプロキシおよび中間体をサポートするように設計されている」ため、HTTPプロトコルとの互換性があると述べています。 互換性を実現するために、WebSocketハンドシェイクはHTTP Upgradeヘッダーを使用して、HTTPプロトコルからWebSocketプロトコルに変更します。
ウィキペディアには、WebSocketについて知っておく必要のあるすべてのことを説明する非常に優れた記事もあります。 ぜひお読みください。
ソケットが実際に機能することを確認した後、私たちはビジネスケースでソケットの機能を調査し始め、壁を次々と叩きました。
プロキシサーバー:一般に、WebSocketとプロキシにはいくつかの異なる問題があります。
- 1つ目は、インターネットサービスプロバイダーとそのネットワークの処理方法に関連しています。 半径プロキシがポートをブロックするなどの問題。
- 2番目のタイプの問題は、セキュリティで保護されていないHTTPトラフィックと長寿命の接続を処理するようにプロキシを構成する方法に関連しています(HTTPSを使用すると影響が少なくなります)。
- 3番目の問題「WebSocketでは、HTTPプロキシではなくTCPプロキシを実行する必要があります。 TCPプロキシは、ヘッダーを挿入したり、URLを書き換えたり、従来のHTTPプロキシに任せていた役割の多くを実行したりすることはできません。」
接続の数:6を中心に展開するHTTPリクエストの有名な接続制限は、WebSocketには適用されません。 50ソケット= 50接続。 50ソケットによる10個のブラウザタブ= 500接続など。 WebSocketはデータを配信するための別のプロトコルであるため、HTTP / 2接続を介して自動的に多重化されることはありません(実際にはHTTP上で実行されるわけではありません)。 サーバーとクライアントの両方にカスタム多重化を実装することは複雑すぎて、指定されたビジネスケースでソケットを有用にすることはできません。 さらに、これによりウィジェットがプラットフォームに結合されます。これは、ウィジェットをサブスクライブするためにクライアント上で何らかのAPIが必要になり、それなしではウィジェットを配布できないためです。
負荷分散(多重化なし) :すべてのユーザーが
n
個のソケットを開く場合、適切な負荷分散は非常に複雑です。 サーバーが過負荷になり、ソフトウェアの実装に応じて新しいインスタンスを作成して古いインスタンスを終了する必要がある場合、「再接続」で実行されるアクションにより、システムが過負荷になる大量の更新とデータの新しい要求がトリガーされる可能性があります。 。 WebSocketは、サーバーとクライアントの両方で維持する必要があります。 現在のサーバーで高負荷が発生している場合、ソケット接続を別のサーバーに移動することはできません。 それらを閉じて、再度開く必要があります。DoS :これは通常、WebSocketに必要なTCPプロキシでは処理できないフロントエンドHTTPプロキシによって処理されます。 ソケットに接続して、サーバーにデータを氾濫させ始めることができます。 WebSocketを使用すると、この種の攻撃に対して脆弱になります。
車輪の再発明:WebSocketを使用すると、HTTPで処理される多くの問題を自分で処理する必要があります。
WebSocketの実際の問題について詳しくは、こちらをご覧ください。
WebSocketのいくつかの良いユースケースは、チャットやマルチプレイヤーゲームであり、実装の問題よりもメリットが重要です。 彼らの主な利点は二重通信であり、私たちはそれを本当に必要としないので、先に進む必要があります。
影響
開発、テスト、スケーリングの観点から、運用上のオーバーヘッドが増加します。 ソフトウェアとそのITインフラストラクチャは、ポーリングとWebSocketの両方を備えています。
モバイルデバイスと両方のネットワークで同じ問題が発生します。 これらのデバイスのハードウェア設計は、アンテナとセルラーネットワークへの接続を維持することにより、オープンな接続を維持します。 これにより、バッテリーの寿命、熱、場合によってはデータの追加料金が減少します。
しかし、なぜモバイルデバイスにまだ問題があるのでしょうか。
デフォルトのモバイルデバイスがインターネットに接続する方法を考えてみましょう。
モバイルネットワークがどのように機能するかについての簡単な説明:通常、モバイルデバイスには、セルからデータを受信する可能性のある低電力アンテナがあります。 このように、デバイスは着信コールからデータを受信すると、コールを確立するために全二重アンテナを起動します。 電話をかけたりインターネットにアクセスしたりするときはいつでも同じアンテナが使用されます(WiFiが利用できない場合)。 全二重アンテナは、セルラーネットワークへの接続を確立し、何らかの認証を行う必要があります。 接続が確立されると、ネットワーク要求を実行するために、デバイスとセルの間に何らかの通信が行われます。 インターネットリクエストを処理するモバイルサービスプロバイダーの内部プロキシにリダイレクトされます。 それ以降、この手順はすでに知られています。DNSにwww.domainname.ext
が実際にどこにあるかを尋ね、リソースへのURIを受信し、最終的にはリソースにリダイレクトされます。
ご想像のとおり、このプロセスはかなりの量のバッテリー電力を消費します。 これが、携帯電話ベンダーが数日の待機時間とわずか数時間の通話時間を提供する理由です。
WiFiがない場合、WebSocketとポーリングの両方で、全二重アンテナがほぼ常に機能する必要があります。 したがって、データ消費量の増加と消費電力の増加に直面し、デバイスによっては熱も発生します。
物事が暗いように見える頃には、アプリケーションのビジネス要件を再検討する必要があるようです。 私たちは何かが欠けていますか?
SSE
MDN経由:
「EventSourceインターフェースは、サーバー送信イベントを受信するために使用されます。 HTTP経由でサーバーに接続し、接続を閉じずにテキスト/イベントストリーム形式でイベントを受信します。」
ポーリングとの主な違いは、接続が1つだけで、イベントストリームが通過し続けることです。 長いポーリングは、プルごとに新しい接続を作成します—ヘッダーのオーバーヘッドやそこで直面したその他の問題をエルゴします。
html5doctor.com経由:
サーバー送信イベントは、サーバーによって発行され、ブラウザーによって受信されるリアルタイムイベントです。 これらはリアルタイムで発生するという点でWebSocketに似ていますが、サーバーからの一方向の通信方法です。
奇妙に見えますが、検討の結果、データの主な流れはサーバーからクライアントへであり、クライアントからサーバーへの流れははるかに少ないです。
これは、データを配信するという主要なビジネスケースに使用できるようです。 プロトコルは単方向であり、クライアントはそれを介してサーバーにメッセージを送信できないため、新しいリクエストを送信することで顧客の購入を解決できます。 これにより、最終的には全二重アンテナがモバイルデバイスで起動するまでの時間遅延が発生します。 ただし、時々発生する状況に対応できます。この遅延は、結局のところミリ秒単位で測定されます。
ユニークな機能
- 接続ストリームはサーバーから来ており、読み取り専用です。
- 特別なプロトコルではなく、永続的な接続に通常のHTTPリクエストを使用します。 箱から出してHTTP / 2を介した多重化を取得します。
- 接続が切断されると、EventSourceはエラーイベントを発生させ、自動的に再接続を試みます。 サーバーは、クライアントが再接続を試みる前のタイムアウトを制御することもできます(詳細については後で説明します)。
- クライアントは、メッセージとともに一意のIDを送信できます。 接続が切断された後にクライアントが再接続を試みると、最後の既知のIDが送信されます。 次に、サーバーは、クライアントが
n
個のメッセージを見逃したことを確認し、再接続時に見逃したメッセージのバックログを送信できます。
サンプルクライアントの実装
これらのイベントは、クリックイベントなど、ブラウザで発生する通常のJavaScriptイベントに似ていますが、イベントの名前とそれに関連するデータを制御できる点が異なります。
クライアント側の簡単なコードプレビューを見てみましょう。
// subscribe for messages var source = new EventSource('URL'); // handle messages source.onmessage = function(event) { // Do something with the data: event.data; };
この例からわかるのは、クライアント側がかなり単純であるということです。 ソースに接続し、メッセージの受信を待ちます。
サーバーがHTTP経由または専用のサーバープッシュプロトコルを使用してデータをWebページにプッシュできるようにするために、仕様ではクライアントに `EventSource`インターフェースを導入しています。 このAPIの使用は、 `EventSource`オブジェクトの作成とイベントリスナーの登録で構成されます。
WebSocketのクライアント実装は、これと非常によく似ています。 ソケットの複雑さは、ITインフラストラクチャとサーバーの実装にあります。
EventSource
各EventSource
オブジェクトには、次のメンバーがあります。
- URL:建設中に設定されます。
- リクエスト:最初はnullです。
- 再接続時間:ミリ秒単位の値(ユーザーエージェント定義の値)。
- 最後のイベントID:最初は空の文字列。
- 準備完了状態:接続の状態。
- 接続(0)
- OPEN(1)
- 休業(2)
URLを除いて、すべてがプライベートのように扱われ、外部からアクセスすることはできません。
組み込みイベント:
- 開ける
- メッセージ
- エラー
接続ドロップの処理
接続が切断されると、ブラウザによって自動的に再確立されます。 サーバーは、接続を再試行または完全に閉じるためにタイムアウトを送信する場合があります。 このような場合、ブラウザは、タイムアウト後に再接続を試行するか、接続が終了メッセージを受け取った場合にまったく再接続を試行しないかのいずれかに準拠します。 かなり単純なようです—そして実際はそうです。
サンプルサーバーの実装
クライアントがそれほど単純な場合、サーバーの実装は複雑かもしれません。
SSEのサーバーハンドラーは次のようになります。
function handler(response) { // setup headers for the response in order to get the persistent HTTP connection response.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); // compose the message response.write('id: UniqueID\n'); response.write("data: " + data + '\n\n'); // whenever you send two new line characters the message is sent automatically }
応答を処理する関数を定義します。
- セットアップヘッダー
- メッセージを作成する
- 送信
send()
またはpush()
メソッドの呼び出しは表示されないことに注意してください。 これは、標準では、例のように2つの\n\n
文字を受信するとすぐにメッセージが送信されると定義されているためですresponse.write("data: " + data + '\n\n');
。 これにより、メッセージがすぐにクライアントにプッシュされます。 データはエスケープされた文字列である必要があり、 data
の最後に改行文字が含まれていないことに注意してください。
メッセージ構築
前述のように、メッセージにはいくつかのプロパティを含めることができます。
- ID
フィールド値にU + 0000 NULLが含まれていない場合は、最後のイベントIDバッファーをフィールド値に設定します。 それ以外の場合は、フィールドを無視してください。 - データ
フィールド値をデータバッファに追加してから、単一のU + 000A LINE FEED(LF)文字をデータバッファに追加します。 - イベント
イベントタイプバッファをフィールド値に設定します。 これにより、event.type
がカスタムイベント名を取得します。 - リトライ
フィールド値がASCII数字のみで構成されている場合は、フィールド値を基数10の整数として解釈し、イベントストリームの再接続時間をその整数に設定します。 それ以外の場合は、フィールドを無視してください。
それ以外は無視されます。 独自の分野を紹介することはできません。
event
が追加された例:
response.write('id: UniqueID\n'); response.write('event: add\n'); response.write('retry: 10000\n'); response.write("data: " + data + '\n\n');
次に、クライアントでは、これはaddEventListener
で次のように処理されます。
source.addEventListener("add", function(event) { // do stuff with data event.data; });
異なるIDを指定する限り、新しい行で区切られた複数のメッセージを送信できます。
... id: 54 event: add data: "[{SOME JSON DATA}]" id: 55 event: remove data: JSON.stringify(some_data) id: 56 event: remove data: { data: "msg" : "JSON data"\n data: "field": "value"\n data: "field2": "value2"\n data: }\n\n ...
これにより、データでできることが大幅に簡素化されます。
特定のサーバー要件
バックエンドのPOC中に、SSEの実装を機能させるために対処する必要のあるいくつかの詳細があることを確認しました。 NodeJS、Kestrel、Twistedなどのイベントループベースのサーバーを使用する場合の最良のシナリオ。 スレッドベースのソリューションでは、接続ごとにスレッドがあります→1000接続= 1000スレッドという考え方です。 イベントループソリューションを使用すると、1000接続に対して1つのスレッドができます。
- HTTPリクエストがイベントストリームMIMEタイプを受け入れることができると言っている場合にのみ、EventSourceリクエストを受け入れることができます。
- 新しいイベントを発行するには、接続されているすべてのユーザーのリストを維持する必要があります。
- ドロップされた接続をリッスンし、接続されたユーザーのリストからそれらを削除する必要があります。
- オプションで、メッセージの履歴を維持して、再接続しているクライアントが見逃したメッセージに追いつくことができるようにする必要があります。
期待どおりに動作し、最初は魔法のように見えます。 アプリケーションが効率的に機能するために必要なすべてのものを取得します。 見た目が良すぎて真実ではないすべてのことと同様に、対処する必要のあるいくつかの問題に直面することがあります。 ただし、実装や回避は複雑ではありません。
レガシープロキシサーバーは、場合によっては、短いタイムアウト後にHTTP接続をドロップすることが知られています。 このようなプロキシサーバーから保護するために、作成者は15秒ごとにコメント行(「:」文字で始まるもの)を含めることができます。
イベントソース接続を相互に、または以前に提供された特定のドキュメントに関連付けたい場合、個々のクライアントが複数のIPアドレスを持つことができ(複数のプロキシサーバーがあるため)、個々のIPアドレスが持つことができるため、IPアドレスに依存することはできません。複数のクライアント(プロキシサーバーを共有するため)。 ドキュメントが提供されるときに一意の識別子を含め、接続が確立されるときにその識別子をURLの一部として渡すことをお勧めします。
また、HTTPチャンクは、特にタイミング要件を認識しない別のレイヤーによってチャンクが実行される場合、このプロトコルの信頼性に予期しない悪影響を与える可能性があることにも注意してください。 これが問題になる場合は、イベントストリームを提供するためにチャンクを無効にすることができます。
HTTPのサーバーごとの接続制限をサポートするクライアントは、各ページに同じドメインへのEventSourceがある場合、サイトから複数のページを開くときに問題が発生する可能性があります。 作成者は、接続ごとに一意のドメイン名を使用するという比較的複雑なメカニズムを使用するか、ユーザーがページごとにEventSource機能を有効または無効にできるようにするか、共有ワーカーを使用して単一のEventSourceオブジェクトを共有することにより、これを回避できます。
ブラウザのサポートとポリフィル:Edgeはこの実装に遅れをとっていますが、節約できるポリフィルを利用できます。 ただし、SSEの最も重要なケースは、IE / Edgeが実行可能な市場シェアを持たないモバイルデバイス向けです。
利用可能なポリフィルのいくつか:
- ヤッフル
- amvtek
- レミー
コネクションレス型プッシュおよびその他の機能
制御された環境で実行されているユーザーエージェント(特定のキャリアに関連付けられた携帯電話のブラウザなど)は、ネットワーク上のプロキシへの接続の管理をオフロードする場合があります。 このような状況では、適合を目的としたユーザーエージェントには、受話器ソフトウェアとネットワークプロキシの両方が含まれていると見なされます。
たとえば、モバイルデバイス上のブラウザは、接続を確立した後、それがサポートネットワーク上にあることを検出し、ネットワーク上のプロキシサーバーが接続の管理を引き継ぐように要求する場合があります。 このような状況のタイムラインは次のようになります。
- ブラウザはリモートHTTPサーバーに接続し、作成者がEventSourceコンストラクターで指定したリソースを要求します。
- サーバーは時折メッセージを送信します。
- 2つのメッセージの間に、ブラウザはTCP接続の維持に関連するネットワークアクティビティを除いてアイドル状態であることを検出し、電力を節約するためにスリープモードに切り替えることを決定します。
- ブラウザがサーバーから切断されます。
- ブラウザはネットワーク上のサービスに接続し、代わりに「プッシュプロキシ」であるサービスが接続を維持するように要求します。
- 「プッシュプロキシ」サービスはリモートHTTPサーバーに接続し、作成者がEventSourceコンストラクターで指定したリソースを要求します(
Last-Event-ID
HTTPヘッダーなどを含む可能性があります)。 - ブラウザを使用すると、モバイルデバイスをスリープ状態にすることができます。
- サーバーは別のメッセージを送信します。
- 「プッシュプロキシ」サービスは、OMAプッシュなどのテクノロジーを使用して、イベントをモバイルデバイスに伝達します。モバイルデバイスは、イベントを処理するのに十分なだけウェイクアップしてから、スリープ状態に戻ります。
これにより、総データ使用量を削減できるため、大幅な電力節約につながる可能性があります。
仕様で定義されている既存のAPIとテキスト/イベントストリームワイヤ形式を(上記のように)より分散された方法で実装するだけでなく、他の該当する仕様で定義されているイベントフレーミングの形式もサポートされる場合があります。
概要
サーバーとクライアントの実装を含む長く徹底的なPOCの後、SSEがデータ配信に関する問題の答えであるように見えます。 それにもいくつかの落とし穴がありますが、修正するのは簡単であることがわかりました。
最終的に、本番環境のセットアップは次のようになります。
NGINXから次の情報を取得します。
- さまざまな場所のAPIエンドポイントにプロキシします。
- HTTP / 2と接続の多重化のようなそのすべての利点。
- 負荷分散;
- SSL。
このようにして、すべてのエンドポイントで個別に行うのではなく、データ配信と証明書を1か所で管理します。
このアプローチから得られる主な利点は次のとおりです。
- データ効率が良い。
- よりシンプルな実装。
- HTTP / 2を介して自動的に多重化されます。
- クライアント上のデータの接続数を1つに制限します。
- プロキシへの接続をオフロードすることにより、バッテリーを節約するメカニズムを提供します。
SSEは、高速更新を提供するための他の方法の実行可能な代替手段ではありません。 モバイルデバイスの最適化に関しては、独自のリーグに属しているように見えます。 代替案と比較して、その実装にオーバーヘッドはありません。 サーバー側の実装に関しては、ポーリングと大差ありません。 クライアントでは、初期サブスクリプションとイベントハンドラーの割り当てが必要なため、ポーリングよりもはるかに簡単です。これは、WebSocketの管理方法とよく似ています。
単純なクライアントサーバー実装を取得したい場合は、コードデモを確認してください。
資力
- 「双方向HTTPでのロングポーリングとストリーミングの使用に関する既知の問題とベストプラクティス」、IETF(PDF)
- W3C勧告、W3C
- 「WebSocketはHTTP / 2を生き残るのだろうか?」、InfoQ、Allan Denis
- 「サーバー送信イベントによるストリーム更新」、Eric Bidelman、HTML5 Rocks
- 「HTML5SSEを使用したデータプッシュアプリ」、Darren Cook、O'Reilly Media