Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Server sent events and browser limits

I have a web application that listens for Server Sent Events. While I was working and testing with multiple windows open, things were not working and I banged my head for several times looking in the wrong direction: eventually, I realized that the problem was concurrent connections.

However I was testing a very limited number and even if I am running the test on Apache (I know, I should use node).

I then, switched browser and noticed something really interesting: apparently Chrome limits Server Sent Events connections to 4-5, while Opera doesn't. Firefox, on the other hand, after 4-5 simultaneous connections, refuses to load any other page.

What is the reason behind this? Does the limit only apply to SSE connections from the same source, or would it be the same if I were to test open them from a different domain? Is there any chance that I am misusing SSE and this is actually blocking the browsers, or this is a known behaviour? Is there any way around it?

like image 574
Sunyatasattva Avatar asked Sep 03 '13 05:09

Sunyatasattva


People also ask

What are server-sent events used for?

Server-Sent Events (SSE) is a server push technology enabling a client to receive automatic updates from a server via an HTTP connection, and describes how servers can initiate data transmission towards clients once an initial client connection has been established.

What is SSE browser?

SSE is designed to use the JavaScript EventSource API to subscribe to a stream of data in any popular browser. Through this interface, a client requests a particular URL to receive an event stream. SSE is commonly used to send message updates or continuous data streams to a browser client.

When to use server-sent events vs WebSockets?

SSE is best used when it's not necessary to send data from client to server. For example, in status updates and push notification applications, the data flow is from the server to the client only. This is what SSE is designed for, so WebSocket would be overkill. It's always wise to use the best tool for the job.

How many SSE connections can a server handle?

This limit is per browser + domain, which means that you can open 6 SSE connections across all of the tabs to www.example1.com and another 6 SSE connections to www.example2.com (per Stackoverflow).


2 Answers

The way this works in all browsers are that each domain gets a limited amount of connections and the limits are global for your whole application. That means if you have one connection open for realtime communication you have one less for loading images, css and other pages. On top of that you don't get new connections for new tabs or windows, all of them needs to share the same amount of connections. This is very frustrating but there are good reasons for limiting the connections. A few years back, this limit was 2 in all browsers (based on the rules in (http://www.ietf.org/rfc/rfc2616.txt) HTTP1.1 spec) but now most browsers use 4-10 connections in general. Mobile browsers on the other hand still needs to limit the amount of connections for battery saving purposes.

These tricks are available:

  1. Use more host names. By assigning ex. www1.domain.com, www2.domain.com you get new connections for each host name. This trick works in all browsers. Don't forget to change the cookie domain to include the whole domain (domain.com, not www.domain.com)
  2. Use web sockets. Web sockets are not limited by these restrictions and more importantly they are not competing with the rest of your websites content.
  3. Reuse the same connection when you open new tabs/windows. If you have gathered all realtime communication logic to an object call Hub you can recall that object on all opened windows like this:

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

  4. or use flash - not quite the best advice these days but it might still be an option if websockets aren't an option.
  5. Remember to add a few seconds of time between each SSE request to let queued requests to be cleared before starting a new one. Also add a little more waiting time for each second the user is inactive, that way you can concentrate your server resources on those users that are active. Also add a random number of delay to avoid the Thundering Herd Problem

Another thing to remember when using a multithreaded and blocking language such as Java or C# you risk using resources in your long polling request that are needed for the rest of your application. For example in C# each request locks the Session object which means that the whole application is unresponsive during the time a SSE request is active.

NodeJs is great for these things for many reasons as you have already figured out and if you were using NodeJS you would have used socket.io or engine.io that takes care of all these problems for you by using websockets, flashsockets and XHR-polling and also because it is non blocking and single threaded which means it will consume very little resources on the server when it is waiting for things to send. A C# application consumes one thread per waiting request which takes at least 2MB of memory just for the thread.

like image 176
Christian Landgren Avatar answered Sep 19 '22 02:09

Christian Landgren


One way to get around this issue is to shut down the connections on all the hidden tabs, and reconnect when the user visits a hidden tab.

I'm working with an application that uniquely identifies users which allowed me to implement this simple work-around:

  1. When users connect to sse, store their identifier, along with a timestamp of when their tab loaded. If you are not currently identifying users in your app, consider using sessions & cookies.
  2. When a new tab opens and connects to sse, in your server-side code, send a message to all other connections associated with that identifier (that do not have the current timestamp) telling the front-end to close down the EventSource. The front-end handler would look something like this:

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

  3. Use the javascript page visibility api to check to see if an old tab is visible again, and re-connect that tab to the sse if it is.

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

  4. If you set up your server code like step 2 describes, on re-connect, the server-side code will remove all the other connections to the sse. Hence, you can click between your tabs and the EventSource for each tab will only be connected when you are viewing the page.

Note that the page visibility api isn't available on some legacy browsers: https://caniuse.com/#feat=pagevisibility

like image 39
NerdSoup Avatar answered Sep 21 '22 02:09

NerdSoup