The "real-time" web in ASP.NET MVC
The “real-time” web is one of the holy grails of software development. It’s the notion that we can provide a native experience through a browser. Users can click or touch buttons, get instant feedback, get app status updates, all without the dreaded postback.
There are quite a few technologies that are starting to bring this to fruition, but like any new shiny tool, we need to use them with wisdom and purpose. With this new hammer, everything may start to look like a nail. Let’s discuss the landscape of these tools within the context of ASP.NET MVC.
Typically when we say “real-time”, the go-to marketing answer is SiglalR as it provides for a constant connection between browser and server, and thus allows the browser to receive events from the server as they happen, not necessarily on next timer tick. (SignalR also has great support for falling back through a stack of technologies that accomplish similar effects: server-side events, long polling, comet, etc.) This type of connection is perfect for things like chat apps where the events from server to client are randomly timed, and you have a few clients.
However, with a constant connection from client to server comes problems with load balancing and scalability. It’s easy to overload your server with SignalR. The premise of the web – and why it generally scales so well – is it generally is very short duration requests that are heavily cached. For example, consider a server that can handle 1000 concurrent requests. (Ok, maybe it’s 100,000, maybe it’s 10, the number is not important.) Now imagine that instead of quickly connecting, getting the answer, and disconnecting, they instead stay connected. Now imagine the 1001th user tries to log on, and the user gets an HTTP 503 because the server is too busy. Oops.
If either of the two conditions above don’t apply – either you have evenly spaced events or you have very many clients – then SignalR is just asking for server pain, and you should create a short-poll app with just regular AJAX and jQuery. For example, if you’re just updating a dashboard page’s graphs, charts, and news feed with the server’s current data for this second, then create a regular $.ajax(…) call every second with a recursive setTimeout() loop or use setInterval() (the latter is easier, the former is safer), and tell the server to cache the answer for 1⁄2 second by using an
[OutputCache] attribute on the Action method or Controller class. Therefore the clients all get the answer quickly and reliably, the server need only process it twice within the second, and the server that can handle only 1000 concurrent requests can now handle far more concurrent browsers than just 1000.
Why output cache for 1⁄2 second instead of matching the client’s 1 second? Because if one client connects at time 0:00 and the next client connects at time 0:00.9, they’ll get a second old result. If this an acceptable data freshness, definitely set the
[OutputCache] to 1 second. If 5 or 15 seconds is acceptable, set both client and server there. If 15 minutes is acceptably stale, set it there. (Note that because we’re discussing this timeout, probably it’s best to store it somewhere configurable and tweak it over time.) But understand an
[OutputCache] of 1 minute means client 2 connecting at 0:59 will have 1 minute old data.
Ultimately, the theme now-a-days in ASP.NET is the theme that’s emerging here: there is no one right way, but rather there are many solutions that are all well supported. The “one ASP.NET” may be better termed “one basket of tools”, each of which is appropriate for a different task and skillset, and inappropriate for others. The day of the hammer searching for a nail is over. Long live the hammer.