Udostępnij za pośrednictwem



April 2012

Volume 27 Number 04

Cutting Edge - Long Polling and SignalR

By Dino Esposito | April 2012

Dino EspositoWe are building more and more sophisticated Web applications on top of a protocol that, paradoxically, was conceived and designed for much simpler forms of interaction. HTTP has no built-in support for state or even security. Its basic assumption is that the client places a request and the Web server issues a response. In sum, that means no request, no response.

Given the current penetration of the Web and the ubiquity of Web solutions, changing the pillars of the Web (HTTP, HTML, JavaScript) is out of the question. But should we consider improving some of those pillars? Of course. Modern applications have specific requirements that push Web protocols and languages to their limit—or beyond.

In recent installments of this column, I discussed a handcrafted implementation of a mechanism that polls the server and reports changes to the client. Last month, I implemented the same idea using the services of an emerging library—SignalR. Now I’ll provide a brief overview of the techniques and a summary of the options you have today. I’ll put SignalR in the spotlight and look at its implementation—and some of its magic.

Polling Considerations

Given the HTTP request/response constraint, polling is the only possible way to set up live communication between a client and Web server. The client pushes requests at its convenience and the server diligently replies. The key to evaluating the effectiveness of polling is the actual meaning you assign to the expression “at its convenience.” There’s always a client request at the core of any solution based on polling. The request stands until the server has replied. Any pending request consumes a browser connection and, more importantly, engages a server thread, making that valuable resource unavailable to other requests. In a nutshell, too-frequent requests build pressure on the Web server.

But isn’t the server’s ability to handle a growing number of requests the whole idea of scalability? In fact, polling doesn’t create new scalability concerns, but it does add a substantial number of new requests to the server that must be taken into account to ensure a scalable and high-performing server. Let’s see how we might implement polling.

AJAX Polling

In my December 2011 (msdn.microsoft.com/magazine/hh580729) and January 2012 (msdn.microsoft.com/magazine/hh708746) columns, I presented a framework for controlling the progress of a remote operation. The entire framework was based on AJAX calls. First, the client invokes a server endpoint to start a potentially lengthy task; next, it sets up a timer to place concurrent calls to another endpoint every 500 milliseconds. As the server task does its job, it updates a known location. The server endpoint that’s invoked periodically simply checks for data in that location and returns any content it finds to the client.

This AJAX-polling pattern works beautifully and is widely employed in many industry scenarios. Most sites that provide news updates or live scores follow this pattern. In my articles, I simply adapted the pattern to the specific scenario of monitoring the progress of a remote operation.

In the end, the problem with AJAX polling is figuring out the right refresh interval—one that represents a good balance between pressure on the server and accuracy of information reported to users. An effective AJAX-polling solution gives a concrete meaning to “at the client’s convenience” in sending requests.

Long Polling

Before AJAX, developers used the meta refresh HTML tag to instruct browsers to refresh a page every given number of seconds. With AJAX polling, you achieve the same thing, but in a much smoother way.

For a growing number of applications, however, this form of polling isn’t enough. AJAX polling almost inevitably introduces a delay between the occurrence of an event on the server and the notification of the event to the client. By tuning the frequency of updates, you can work out good solutions, yet AJAX polling can’t deliver that constant and continuous connection that many (mostly social) applications seem to need today.

The solution these days seems to be long polling. Curiously, when AJAX emerged years ago, smarter AJAX polling replaced long polling because the latter was not very effective over the Web. Long polling is about doing over the Web the same things you do in a desktop scenario. With long polling, the client places the request and the server doesn’t reply until it has information to return. The Web client keeps a pending connection that’s closed only when some valid response can be returned. That’s exactly what you want nowadays—except that it has the potential of slowing down the Web server.

Long polling places a smaller number of requests to the server compared with AJAX polling, but each request could take much longer. Over time, this can be detrimental to the health of the Web server because it keeps server threads engaged until a response can be generated. With fewer worker threads available at a given time, the Web server inevitably gets slower in responding to any other requests it receives. To be effective, long polling needs some serious implementation work and advanced multithreaded and parallel programming skills. So, for years, long polling wasn’t really an option, but it didn’t matter because there was no demand for continuous connectivity.

Today, with the Task Parallel Library in the Microsoft .NET Framework 4 and other facilities in ASP.NET for building asynchronous HTTP handlers, long polling has become a viable option. Overall, handcrafting a long-polling framework is still harder than a corresponding AJAX-polling framework. You need a well-designed server environment that doesn’t tax the Web server and a client environment that can support long polling. Long polling, in fact, refers to a macro client/server operation that develops over the Web and necessarily over a number of classic HTTP request/response packets. The client must be smart enough to reissue an immediate new request until the macro operation terminates. I’ll return to this point later.

SignalR to the Rescue

SignalR is a Microsoft framework specifically designed to facilitate real-time client/server communication. It provides an effective implementation of long polling that doesn’t have a deep impact on the server, and at the same time ensures that clients are appropriately updated about what’s going on remotely. Figure 1shows an excerpt from the code I presented in my last column. The BookingHub class is the method you invoke from the Web browser to start the macro operation “book the flight.”

Figure 1 A SignalR Class that Performs a Multistep Operation

public class BookingHub : Hub
{
  public void BookFlight(String from, String to)
  {
    // Book first leg
    Clients.displayMessage(
      String.Format("Booking flight: {0}-{1} ...", from, to));
    BookFlightInternal(from, to);
    // Book return
    Clients.displayMessage(
      String.Format("Booking flight: {0}-{1} ...", to, from));
    BookFlightInternal(to, from);
    // Book return
    Clients.displayMessage("Charging credit card ...");
    ChargeCreditCardInternal();
    // Some return value
    Clients.displayMessage("Flight booked successfully.");
  }
}

This is clearly a multistep operation, and for each step the class sends a notification to the client. How many HTTP requests are involved? Let’s take a look with Fiddler (see Figure 2).

The Full Stack of HTTP Requests for the Flight-Booking Operation
Figure 2 The Full Stack of HTTP Requests for the Flight-Booking Operation

Inside a SignalR Operation

The first request is triggered when you call the method start from the client page; this identifies the client to the SignalR back end. It should be noted that the first request is a special negotiation request. It will always be AJAX regardless of the final transport negotiated for the connection. Any SignalR Web page includes code like the following that runs when a page loads:

$(function () {
  var bookingHub = $.connection.bookingHub;
  bookingHub.start();
}

In this way, the page declares its intention to open a connection to invoke the services of the server-side bookingHub object. Note that it’s SignalR that does the magic of creating a client object according to the public interface of the server-side hub object. The name of the JavaScript object matches the name of the server object. However, you can use the HubName attribute to modify the name being used on the client:

[HubName("booking")]
public class BookingHub : Hub
{
  ...
}

The startup request returns a client ID that the client will pass along with any successive requests.

{"Url":"/signalr","connectionId":"2a119962-edf7-4a97-954b-e74f2f1d27a9"}

Next, the client library places another request for connection. This is the “connect” request, which will result in the OnConnect event being raised for the connection on the server. Figure 3 shows a couple of connect requests.

Reiterating Connection Requests
Figure 3 Reiterating Connection Requests

The first completed in two minutes; the second is still pending. This is the essence of long polling—the client has a channel continuously open with the server. The server times out the request after two minutes if no action is performed on the server that requires sending data back to the client. In the flight-booking application UI, there’s a button the user can click to start booking the flight. When the user clicks the button the following code executes:

bookingHub.bookFlight("fco", "jfk");

If you created the example application from last month’s column, you should have linked this code to the click handler of the page button.

The bookingHub object belongs to a script that SignalR downloads via the signalr/hubs URL, as shown in Figure 3. bookingHub is a plain proxy object that hides the complexity of implementing a long-polling pattern. Clicking the button triggers a new request to the server where the client ID is embedded. When the server receives a call from a client that has a pending call, it ends the pending call and starts processing the new request, as shown in Figure 4.

A Multistep Operation Is Taking Place
Figure 4 A Multistep Operation Is Taking Place

Figure 4 shows a pending request (signalr/send), two completed requests and another pending request. The signalr/send request is the original call to the macro operation for booking the flight, whose source code is shown in Figure 1.

The code in Figure 1 calls the client at various stages to notify it of any progress, like so:

Clients.displayMessage(...);

At the same time a request for signalr/send is placed, another request starts to wait for notifications. This request waits until there’s some data to send back. Any call to Clients in the server code completes the request for notification and immediately triggers another one from the client, so the sequence in Figure 4 means that two steps have been completed and two progress messages have been received by the client. The server process is now busy trying to accomplish the third step. By the end of the code in Figure 1, all of the requests in Figure 4 will have completed and the situation will return to one similar to what’s shown in Figure 2.

Beyond Long Polling

If you compare the behavior of long polling as implemented in SignalR with the steps I indicated in my articles about AJAX polling, you should recognize a common pattern—the Progress Indicator pattern. AJAX polling and long polling are two different implementations of the same pattern. Which one is more effective depends on the configuration of the Web server and requirements of the application. It’s your call. In any case, if you opt for long polling, you need SignalR or an analogous library. If you want to build your own framework, I suggest you opt for the numerous-but-quick calls of AJAX polling versus the fewer-but-longer calls of long polling.

Building an effective long-polling framework can be tricky. With AJAX polling you probably don’t get continuous connectivity, but you also don’t risk slowing down the server. With this in mind, I probably won’t go around and update any real-time Web server I take care of to SignalR. However, once SignalR is released, I don’t see any reason not to use it for new applications.

Finally, it’s worth mentioning that SignalR now supports other higher-level transports besides long polling. In the latest source, you also find support for WebSockets on Windows 8 servers; Server Sent Events on Chrome, Firefox, Opera and Safari; and Forever Frame on Internet Explorer. The library starts at the beginning of the list and keeps falling back until a supported transport is found. So, in the end, long polling will actually rarely be used in most cases.


Dino Esposito  is the author of “Programming ASP.NET 4” (Microsoft Press, 2011) and “Programming ASP.NET MVC 3” (Microsoft Press, 2010), and coauthor of “Microsoft .NET: Architecting Applications for the Enterprise” (Microsoft Press, 2008). Based in Italy, Esposito is a frequent speaker at industry events worldwide. Follow him on Twitter at Twitter.com/despos.

Thanks to the following technical expert for reviewing this article: Damian Edwards