Friday, July 24, 2009

Pub/sub sample using HTTP polling duplex WCF channel in Microsoft Silverlight 3

NOTE: Many people reading this post are really looking for a publish\subscribe solution for Silverlight to implement a web chat or a stock quote application for the browser, a multiplayer online game, or some form of a web based collaboration tool, among others. If that fits your profile, you may want to check out http://laharsub.codeplex.com. Laharsub is an open source pub\sub message server for web clients, including Silverlight. It aspires to address the needs of people like you, who are interested in the subject matter of this post as well as willing to contribute your scenarios and requirements by leaving a comment. Enjoy!

This sample demonstrates how a Microsoft Silverlight 3 application can consume asynchronous data notifications from the server using the polling duplex protocol of Windows Communication Foundation (WCF). I am also discussing selected aspects of the implementation, in particular related to performance. The full source code and Visual Studio solution of the sample is available for download. Please also check the Silverlight development environment prerequisites.

The application follows a publisher/subscriber architecture and consists of three components:

  • PubSubService.svc is a WCF web service that manages client subscriptions to topics and sending asynchronous notifications to clients when data is published to the topic they are subscribed to. The service exposes an endpoint with a binding based on the polling duplex protocol. The server side implementation of the protocol is available through the PollingDuplexHttpBinding class in the System.ServiceModel.PollingDuplex.dll .NET Framework 3.5 library shipped as part of Microsoft Silverlight 3 SDK.  
  • PubSubClient.aspx is an ASP.NET page that contains a Microsoft Silverlight 3 client application that can act as both a subscriber and a publisher. The application allows the user to subscribe to notifications related to a particular topic as well as publish data to that topic. The client communicates with the PubSubService.svc service using a WCF proxy and the client side implementation of the PollingDuplexHttpBinding, which ships as a Silverlight 3 extension assembly System.ServiceModel.PollingDuplex.dll within the Microsoft Silverlight 3 SDK.
  • PublisherClient.aspx is an ASP.NET page that contains a Microsoft Silverlight 3 client application that can act as a publisher only. The application allows the user to start publishing data to a particular topic at specified frequency, therefore causing a stream of regular notifications to be sent to subscribed clients. The client communicates with the backend PubSubService.svc using the same mechanism as PubSubClient.aspx.

There are two key scenario types that can be demonstrated using this sample: collaboration and broadcast.

Collaboration Scenario

In the collaboration scenario we have a small set of clients (typically 2) acting as both publishers and subscribers of the same topic. An example of a collaboration scenario is a chat. In order to simulate the collaboration scenario, open the PubSubClient.aspx client application in two or more browser windows, subscribe to the same topic in each of them, and then start publishing to the topic by typing text in the provided text box in any of the clients. You should observe the text being propagated through notifications to all other clients. 

Broadcast Scenario

In the broadcast scenario we have multiple clients subscribed to the same topic, and a small number of publishers (typically 1) publishing to the topic. An example of a broadcast scenario is propagating stock quotes from a single source to multiple client applications. In order to simulate the broadcast scenario, open the PubSubClient.aspx client application in multiple browser windows and subscribe to the same topic. Then open the PublisherClient.aspx client application in a single browser window and start publishing to the same topic you have subscribed to. You should observe periodic notifications arriving at all subscribed clients.

Key implementation aspects

Server

The PubSubService.svc encapsulates the pub/sub server logic. It is a WCF duplex service exposed over the PollingDuplexHttpBinding that shipped with Microsoft Silverlight 3. The binding implements a comet-style long polling protocol which enables firewall traversal by leveraging HTTP protocol to enable duplex communication.

A few implementation aspects of the service are worth calling out given their implications for performance of the service.

In the broadcast scenario or a collaboration scenario that involves more than 2 participants, identical messages are often sent to several clients. Given that a substantial portion of the cost of sending a message is related to serializing its content, it is worthwhile to pre-serialize a message once and then send a copy of it multiple times. In order to accomplish this, the callback contract of a pub/sub service should take a Message as a parameter as opposed to typed parameters. This allows the message to be pre-serialized and converted to a MessageBuffer using TypedMessageConverter. Then, for every notification to be sent, the MessageBuffer can be used to create a clone of the Message without incurring the serialization cost. This is how the WCF service contract of the pub/sub service looks like:

[ServiceContract(CallbackContract = typeof(INotification))]
public interface IPubSub
{
    [OperationContract(IsOneWay = true)]
    void Subscribe(string topic);

    [OperationContract(IsOneWay = true)]
    void Publish(string topic, string content);
}

[ServiceContract]
public interface INotification
{
    [OperationContract(IsOneWay = true,
                                AsyncPattern = true, Action="*")]
    IAsyncResult BeginNotify(Message message,
                                       AsyncCallback callback, object state);
    void EndNotify(IAsyncResult result);
}

[MessageContract]
public class NotificationData
{
    public const string NotificationAction = "http://…";

    [MessageBodyMember]
    public string Content { get; set; }
}

When a notification is to be sent to multiple clients over the INotification service contract, the message is first pre-serialized using a TypedMessageConverter:

TypedMessageConverter messageConverter = 
    TypedMessageConverter.Create(
        typeof(NotificationData),
        NotificationData.NotificationAction,
        "http://…");

Message notificationMessage = messageConverter.ToMessage(
    new NotificationData { Content = “hello” });
MessageBuffer notificationMessageBuffer =
    notificationMessage.CreateBufferedCopy(65536);

Then a copy of the message is sent to all clients, thus avoiding the serialization cost on every send:

foreach (INotification callbackChannel in clientsToNotify)
{
    try
    {
        callbackChannel.BeginNotify(
            notificationMessageBuffer.CreateMessage(), 
            onNotifyCompleted, callbackChannel);
    }
    catch (CommunicationException) { }
}

Another aspect to emphasize in how the notifications are sent is related to the use of asynchronous API INotification.BeginNotify/EndNotify. Sending a large number of notifications is a high latency activity. Using asynchronous APIs to do so results in more efficient use of system resources compared to the use of synchronous APIs. Alternatively, synchronous APIs could be used if each of the notifications was scheduled to be sent on a separate worker thread from the thread pool. Measurements of both methods indicate the performance difference between them is negligible.

Finally, the concurrency mode of the WCF service is set to ConcurrencyMode.Multiple, and instance mode to InstanceMode.Single. This requires explicit synchronization code to be added around access to critical resources (i.e. data structures related to subscriptions), but the extra effort pays off in reduced contention of concurrent requests to the service:

[ServiceBehavior(
    ConcurrencyMode = ConcurrencyMode.Multiple, 
    InstanceContextMode = InstanceContextMode.Single)]
public class PubSubService : IPubSub
{
    // …       
}

There are several more performance considerations for setting up a pub/sub service based on the HTTP polling duplex protocol. I will cover them in an upcoming post dedicated to performance tuning of such scenario.

Client

The key implementation aspect of the pub/sub service client in the sample is the use of the Add Service Reference feature of Visual Studio to automatically generate a proxy to such a service. This is a major usability improvement from Silverlight 2 to Silverlight 3. After the proxy has been added to the client project using the Add Service Reference feature of Visual Studio (or the slsvcutil.exe command line tool offering corresponding functionality), consuming asynchronous notifications from the sever is as easy as hooking up events on the generated proxy:

PubSubClient client = new PubSubClient(
    new PollingDuplexHttpBinding(),
    new EndpointAddress(“http://…”));
this.client.NotifyReceived +=
    new EventHandler<NotifyReceivedEventArgs>(NotifyReceived);
this.client.SubscribeAsync(“my topic”);

Thanks to this feature, the entire pub/sub client application is around 100 lines of C# code.

Thursday, July 9, 2009

Adoption of WCF technologies in Silverlight 2 applications on the web

Microsoft Silverlight 2 is the first version of the .NET client development platform which contains Windows Communication Foundation (WCF) components. The Silverlight platform in this version is primarily targeting rich internet application development. Such applications execute in a security sandbox that limits the choices for data exchange with the outside world. For example, a Silverlight application does not have free access to the file system except for an isolated local storage. Any Silverlight application that requires external data must use a communication mechanism to obtain it from the web and a serialization mechanism to parse and manipulate the data. Silverlight 2 offers several communication and data serialization technologies that facilitate these scenarios.

The choice of communication and serialization components in Silverlight 2 was based on developer community data and the target scenarios for the platform. Some time after the platform has shipped in October 2008 I decided to measure the level of adoption of the various technologies we added to Silverlight 2. We intended to use this data to validate our original assumptions as well as inform future releases.

The methodology of this measurement was based on finding a statistically significant sample of Silverlight 2 applications deployed on the web and statically analyzing these applications to identify the platform features they use. I was interested in several communication technologies:

  • WebClient
  • HttpWebRequest
  • ClientBase (WCF proxy)
  • ChannelFactory (direct use of WCF)
  • Sockets
  • Polling duplex
  • Astoria

I was also looking for several serialization technologies:

  • DataContractSerializer
  • DataContractJsonSerializer
  • XmlSerializer
  • JsonValue
  • Syndication
  • XElement
  • XmlReader

Lastly, I was interested in the backend technology of services the clients were communicating with (ASMX, WCF, or other), as well as whether these calls were same-domain or cross-domain.

In February 2009 (about 5 months after Silverlight 2 released) I used a custom web crawl engine to index over 40,000 web pages, which yielded 772 Silverlight 2 applications across 335 web sites. Then I downloaded and disassembled the managed application code from the XAP into IL to statically analyze it for sentinel constructs of the technologies in question. I discuss the results below.

The high level findings are as follows:

  • 53% of applications used one or more of the communication technologies
  • 47% of applications used one or more of the serialization technologies (counting only direct use by the application code; this number does not include indirect use via other components, e.g. a WCF proxy)
  • 41% of applications used neither communication or serialization technologies

The fact that over half of all Silverlight applications used at least one communication technology emphasizes the importance of the communication component in a sandboxed web application.

The table below contains the breakdown of the adoption of individual communication technologies across applications that use at least one of the communication technologies (i.e. across the 53% of Silverlight 2 applications I found through the web crawl).

Rank Adoption Technology
1 63.6% WebClient
2 44.9% ClientBase (WCF proxy)
3 18.0% HttpWebRequest
4 2.2% Socket
5 1.7% Astoria
6 0.7% ChannelFactory (WCF)
7 0.2% Polling duplex

Similarly, the table below contains the serialization technology adoption breakdown across application that use at least one of the serialization technologies (i.e. across the 47% of Silverlight 2 applications I found through the web crawl). The number represents only direct use of these serialization mechanisms by the application code. In particular, it excludes indirect use by web service proxies like ClientBase (WCF) the application may be using.

Rank Adoption Technology
1 70.3% XElement
2 43.5% XmlReader
3 8.8% XmlSerializer
4 6.9% DataContractJsonSerializer
5 3.2% DataContractSerializer
6 1.9% JsonValue
7 1.6% Syndication

WebClient is the mostly adopted communication technology, followed by the WCF proxy (ClientBase). WebClient is mainly used to download entire files or loosely structured XML data, which is later processed using XElement as a serializer of choice. WCF is primarily used as a mechanism to access strongly typed data. One aspect to note is that WCF proxy internally uses DataContractSerializer (another WCF technology) as a default serialization engine (something WebClient is not doing with XElement), which means that DataContractSerializer has a total adoption of around 25.3% across all surveyed Silverlight 2 applications (47% * 3.2% + 53% * 44.9%; this assumes no overlap between applications using DataContractSerializer directly and ClientBase at the same time, which is an approximation).

Analysis of the backend technology of the services that Silverlight 2 clients are communicating with indicates 52% of them are based on WCF technology, 42% are based on ASMX, and the technology of the remaining 6% could not be determined. Furthermore, analysis of the URLs of the web service calls indicates 68% of calls is cross-domain while only 32% are same-domain. The large share of cross-domain calls is an interesting finding in itself. It also seems to support a hypothesis that the (still) large share of ASMX as the backend technology has to do with the existence of legacy services.

Overall the data from this research informed several decisions related to investments in subsequent releases of Silverlight, but this is another story. As a matter of fact Silverlight 3 is coming out within hours of this writing, check out http://www.microsoft.com/silverlight/ for an announcement.

My Photo
My name is Tomasz Janczuk. I am currently working on my own venture - Mobile Chapters (http://mobilechapters.com). Formerly at Microsoft (12 years), focusing on node.js, JavaScript, Windows Azure, and .NET Framework.