web-dev-qa-db-ja.com

サーバー送信イベントとブラウザーの制限

サーバー送信イベントをリッスンするWebアプリケーションがあります。複数のウィンドウを開いた状態で作業およびテストを行っているときに、問題が発生し、間違った方向を数回頭を叩きました。最終的に、問題は同時接続であることに気付きました。

しかし、私は非常に限られた数をテストしていて、Apacheでテストを実行している場合でも(ノードを使用する必要があることはわかっています)。

次に、ブラウザを切り替えて、本当に興味深いことに気づきました。どうやらChromeはServer Sent Events接続を4-5に制限していますが、Operaは制限していません。一方、Firefoxは、4〜5回の同時接続後、ロードを拒否しますany他のページ。

この背後にある理由は何ですか?制限は同じソースからのSSE接続にのみ適用されますか、それとも別のドメインからテストオープンした場合も同じですか? SSEを誤用していて、これが実際にブラウザをブロックしている可能性はありますか、それとも既知の動作ですか?それを回避する方法はありますか?

28
Sunyatasattva

これがすべてのブラウザで機能する方法は、各ドメインが取得する接続数が制限されており、その制限はアプリケーション全体でグローバルです。つまり、リアルタイム通信用に1つの接続を開いている場合、画像、CSS、およびその他のページをロードするための接続が1つ少なくなります。その上、新しいタブやウィンドウの新しい接続は取得されません。それらすべてが同じ量の接続を共有する必要があります。これは非常に苛立たしいことですが、接続を制限するのには十分な理由があります。数年前、この制限はすべてのブラウザで2でした(( http://www.ietf.org/rfc/rfc2616.txt )HTTP1.1仕様のルールに基づく)が、現在はほとんどブラウザは一般に4〜10の接続を使用します。一方、モバイルブラウザでは、バッテリーを節約するために接続数を制限する必要があります。

これらのトリックが利用可能です:

  1. より多くのホスト名を使用します。 exを割り当てることによって。 www1.domain.com、www2.domain.comでは、ホスト名ごとに新しい接続を取得します。このトリックはすべてのブラウザで機能します。ドメイン全体(www.domain.comではなくdomain.com)を含むようにCookieドメインを変更することを忘れないでください
  2. Webソケットを使用します。 Webソケットはこれらの制限によって制限されておらず、さらに重要なことに、Webソケットは他のWebサイトコンテンツと競合していません。
  3. 新しいタブ/ウィンドウを開くときに同じ接続を再利用します。すべてのリアルタイム通信ロジックをオブジェクト呼び出しハブに収集した場合、開いているすべてのウィンドウでそのオブジェクトを次のように呼び出すことができます。

    window.hub = window.opener ? window.opener.hub || new Hub()

  4. またはフラッシュを使用します-最近の最善のアドバイスではありませんが、WebSocketがオプションでない場合は、それでもオプションになる可能性があります。
  5. 新しいリクエストを開始する前に、キューに入れられたリクエストをクリアできるように、各SSEリクエストの間に数秒の時間を追加することを忘れないでください。また、ユーザーが非アクティブである秒ごとにもう少し待機時間を追加します。アクティブなユーザーにサーバーリソースを集中させる方法。また、ランダムな数の遅延を追加して、 Thundering Herd Problem を回避します。

JavaまたはC#などのマルチスレッドおよびブロッキング言語を使用する場合に覚えておくべきもう1つのことは、アプリケーションの残りの部分に必要な長いポーリング要求でリソースを使用するリスクがあります。たとえば、C#では各要求セッションオブジェクトをロックします。これは、SSEリクエストがアクティブな間、アプリケーション全体が応答しないことを意味します。

NodeJsは、すでに理解しているように多くの理由でこれらの点で優れています。NodeJSを使用している場合は、websocket、flashsockets、およびXHRポーリングを使用してこれらすべての問題を処理するsocket.ioまたはengine.ioを使用します。また、非ブロッキングでシングルスレッドであるため、送信を待機しているときにサーバー上のリソースをほとんど消費しません。 C#アプリケーションは、待機中の要求ごとに1つのスレッドを消費し、スレッド専用に少なくとも2MBのメモリを消費します。

35

あなたは同時接続の数について正しいです。

このリストで最大値を確認できます: http://www.browserscope.org/?category=network

残念ながら、多重化や異なるホスト名の使用を除いて、回避策は見つかりませんでした。

3
avetisk

この問題を回避する1つの方法は、すべての非表示のタブの接続をシャットダウンし、ユーザーが非表示のタブにアクセスしたときに再接続することです。

私は、この簡単な回避策を実装できるようにしたユーザーを一意に識別するアプリケーションを使用しています。

  1. ユーザーがsseに接続するときは、タブがロードされたときのタイムスタンプとともに、識別子を保存します。現在アプリでユーザーを特定していない場合は、セッションとCookieの使用を検討してください。
  2. 新しいタブが開いてsseに接続したら、サーバー側のコードで、その識別子に関連付けられている他のすべての接続(現在のタイムスタンプを持たない)にメッセージを送信して、フロントエンドにEventSourceを閉じるように指示します。フロントエンドハンドラーは次のようになります。

    myEventSourceObject.addEventListener('close', () => { myEventSourceObject.close(); myEventSourceObject = null; });

  3. Javascriptページの可視性APIを使用して、古いタブが再び表示されるかどうかを確認し、表示されている場合はそのタブをsseに再接続します。

    document.addEventListener('visibilitychange', () => { if (!document.hidden && myEventSourceObject === null) { // reconnect your eventsource here } });

  4. 手順2で説明したようにサーバーコードを設定した場合、再接続時に、サーバー側のコードによってsseへの他のすべての接続が削除されます。したがって、タブ間をクリックすると、各タブのEventSourceは、ページを表示しているときにのみ接続されます。

一部のレガシーブラウザではページ表示APIを使用できないことに注意してください: https://caniuse.com/#feat=pagevisibility

1
NerdSoup