SignalR – handling browser connection limits
- Processes, standards and quality
- Technologies
- Others
My motivation for writing this article is to show that even a small new feature can have impact on the overall site availability. You can try to use right technology. How to do this?The answer is as usual ‘it all depends’. What I would like to do, in this article, is not to provide the final answer, but to show you options, solutions and ideas for a given use case.
Let’s start our journey from some ASP.NET website. Some content is changing relatively often but site users cannot see those changes until they refresh the page (and it’s not a single page application). You are asked to dynamically update client side content. On your backend you actually know when the new data are saved into database. Probably, in the future, a greater number of pages will be processed in that manner.
Usually, the solution would be a JavaScript timer, which will periodically and thoughtlessly download data from the web service; it will result in an overwhelmed service. It also increases network load – combination of refreshing every couple of seconds and leaving the site open for half a day may result in a transfer of dozens or hundreds of megabytes per each tab, per each user. The web browsers also doesn’t ‘like’ having too many timers running.
We can also use a brand new real-time library, SignalR! Perhaps, you or your manager care about using the newest technology, or maybe this solution was imposed on you. Your task is to use a library that facilitates a two-way communication. It won’t be a site which will check up if there is some new data to display, instead you will let it know as soon as new data arrives.
Having in mind that not all browsers support this library (like the loved one, IE7), probably it will be necessary to implement those JavaScript timers as a fall-back, if required. Though, the library runs from IE8, and WebSockets protocol is available from IE10. For more details see supported platforms.
For some reasons it is decided to use SignalR. If we know that each client will use the newest version of their browser that is supported by WebSockets, then we either write start-up or we can display that „IE is not supported” ;). In this situation you can stop reading – congratulations! Otherwise, I recommend getting familiar with the rest of this article.
We have our sample website with SignalR. The website content is immediately refreshed when we save new data in our backend. It all looks great until it turns out that the website crashes when more than six tabs are open. New tabs don’t load.
At first, we blame local IIS, which is limited to 10 connections at a time. Server IIS doesn’t have this kind of limitation. However, locally it still crashes with 7th tab, so it’s not it.
Every browser appears to have a maximum number of simultaneous connections: see browserscope.
And it would be right, as for chrome the limit is 6 simultaneous connections to a single domain. Limitation in number of requests per domain results from HTTP 1.1 specification – it says that there are maximum two requests, however in real life there are more of them – it depends on a browser.
It also turns out that hosting team hasn’t installed WebSockets in a development environment, so that transportation layer is not available for you. Note that any other transportation layer (such as Server Sent Events (SSE), Forever Frame, and long-pooling) counts to the maximum connection limit.
Even if WebSockets are installed on a server there are situations, in which other transportation layer is used, e.g. it depends on the browser support, usage of JSONP, etc. For more details see transport selection process.
Since you’ve discovered that a web browser has a limited number of connections, you’re probably considering whether you can ignore this issue. After all, you never know in which browser and in how many tabs a person will open a given site (and your business clients might want adjustments for IE7-9).
Nothing can be expected by the users. Is it possible to ignore an error: „the site doesn’t load”, caused „by design” and by exhausted connection pool by other tabs?
Let’s think about possible solutions:
- Ignore the issue
- Choose transport layer which won’t cause exhaustion of a limit of connections
- Use a dedicated/commercial solution
- Count active connections in a browser
- Limit number of connections
- Monitor active connections
- Make an attempt to get around the issue by appropriate usage of domains
Let’s go through the following solutions:
- Ignore the issue
Who will open my application in more than six tabs? It makes sense to ignore it when it comes to chat or single page application, doesn’t it?
- Choose transport layer which won’t cause exhaustion of a limit of connections
We can leave only WebSockets as a transport layer and ignore older browsers – nothing will crash as WebSockets are not included in the limited pool of simultaneous connections from the browser.
- Use a dedicated/commercial solution
An example of commercial solution for live streaming is http://www.lightstreamer.com/. My colleague started 10 tabs in chrome with their demo and they all worked. The manufacturer boasts that one server supports 20 million simultaneous connections, but I haven’t tested what transportation layer it uses.
- Count active connections in a browserSince it is a browser that limits a number of simultaneous connections, we can try to write logic that will monitor them. We can also count active tabs in JavaScript and try to send statements about opening and closing them with the use of e.g. localStorage. Alternatively, we can choose more solid solution and use database to count connections.
- Limit number of connections
We can try to count connections in a database and show a message while displaying nth tab: „sorry, you have opened too many tabs, this one will not support our functionalities” e.g. Cannot have many tabs open with SignalR.
- Monitor active connections
Since a browser is limited to six connections, we can try to manually monitor how many connections are supported by a given browser. We can use localStorage to send statements between opened windows. One window will maintain active connection and as soon as a client method SignalR is called, localStorage will inform other windows, which have subscribed its actions, about that. This is kind of a Producer-Consumer model in a browser.
- Make an attempt to get around the issue by appropriate usage of domains
- Count active connections in a browserSince it is a browser that limits a number of simultaneous connections, we can try to write logic that will monitor them. We can also count active tabs in JavaScript and try to send statements about opening and closing them with the use of e.g. localStorage. Alternatively, we can choose more solid solution and use database to count connections.
As the limit in a browser refers only to one domain, when it comes to SignalR connections, we can try to manage them by:
- using another domain (however, then we have to deal with CSFR);
- using sub-domain (however, if a website uses https, it’s better to have SSL certificate *.mywebsite.com).
Thanks to that the majority of pages will load properly in case of a greater number of tabs opened.
It is good to know that we have so many options, however what about hard facts?
- What may involve support for different domains?
- How to use localStorage with other presented solutions?
- A closed window issue.
What may involve support for different domains?
We can use another domain to communicate with SignalR than the one dedicated to our site. Its advantage is that in case of exceeding the maximum number of connections, the page will load normally, and only the connection to the hubs may have issues. This may be a compromise, in which the main functionality of the site would never fall due to the connection limit with a minimum effort to repair the extreme case.
Wildcard domain registration
We could register a domain with a wildcard, * .abc.com type and create dynamic sub-domains (a1.abc.com, a2.abc.com, etc.) depending on the load of connections. This kind of solution seems to be more apache friendly. I don’t know how it would look like in IIS. Here you have the script with admin rights, which would add website on the fly „%windir%\system32\inetsrv\appcmd” add site /in < „abc.mywebsite.com_generated.xml”. Yes, that would be overkill, however it is possible to modify sites with commands line by using appcmd.
A single domain registration
We can also try to use only one domain, but I don’t know if it is possible/more apache-wise. I guess, there is still a problem with supporting wildcard domains in IIS.
Theoretical example:
* It is enough to use a simple domain, such as mydomain.com.
* It is necessary to configure wildcard A-record, so that it would take over even non existing sub domains.
Every request to non-existing sub domain will be redirected to a given, existing address, such as www.mydomain.com.
* You need to have IIS Rewriter for such a website, which seems to be simple, however SignalR runs on Owin, so you probably need to use middleware.
How to use localStorage with other presented solutions?
You can have a tab with a connection to SignalR and promote information about the methods called by the server to the other tabs using localStorage events. Everything seems to be simple to the point where you realize that our „Main” tab can also be closed. If there are more open tabs – which one would take over communications, or even know about breaking the connection to the hub?
- localStorage + static variable with the current connections list
SignalR server allows us to connect to the OnConnected/OnReconnected/OnDisconnected event. Due to that it is possible to determine a number of currently connected clients. Immediately reject this idea, if your website is on WebFarm and you don’t want to analyse whether the calls or each instance are properly counted (I don’t think they will be).
- localStorage + a list of connections in a database
Every user (even the anonymous one) maybe identified by some UUID/GUID from cookies. It is possible to count a number of active connections and limit them to one in a database. Additionally, we can add timer at the server side, which will monitor whether all connections are still active, in case of browser malfunctioning. (https://github.com/DamianEdwards/NDCLondon2013/blob/master/UserPresence/PresenceMonitor.cs)
Closed window issue.
Limiting connection to one window in a browser may seem to be a great idea until you realize that a user can close the one with active connection.
Theoretical algorithm resolving this issue:
- A site during loading (onload) checks in a database e.g. by WebApi whether a site has already an active connection, if not then it starts connection with SignalR.
- There was a switching between tabs (onfocus)- same as above. If someone closed the tab with the connection, switching to another tab will verify whether the connection has been closed – and open it again, if needed.
- When the page closes (beforeunload), it also closes the connection to the hub. It is necessary to get a new „connectionID” with SignalR – otherwise the tab wouldn’t know to whom the active connection belongs (you will have to wait for connection timeout)
- We can add an event, e.g. after a minute of activity site downloads the most recent copy of the data at once. It is also worth remembering that there is always a non-zero probability that between loading the site and starting SignalR, a notification of new data occurred that hasn’t been noticed earlier.
Conclusions
SignalR is a rather new technology. We have to be aware of its advantages and limitations. Depending on the fact that we write a brand new site based on the newest technologies or we integrate it with ten-year-old sites, which are used by corporations with IE8, we have to consider various risks. A client should be aware of those risks. On the other hand, an attempt to bypass some of the disadvantages adds redundant complexity, which would be better replaced using older methods, such as periodic validation of the data by the timer. By the way, remember that any redundant logic added to SignalR can impair or greatly reduce its performance.
Videos I can recommend:
- Examples of SignalR usage: http://www.ndcvideos.com/#/app/video/161
- Why scaling and more servers mean slower not faster for the real-time: http://channel9.msdn.com/Events/Build/2013/3-502