Home > Atmosphere, Comet, JQuery, Websocket > Friday’s Tricks #4: Improving Websocket/Comet performance using Delayed/Aggregated Server Side Events

Friday’s Tricks #4: Improving Websocket/Comet performance using Delayed/Aggregated Server Side Events

This week I will explain how you can significantly improve the performance of your WebSocket/Comet application using delayed and aggregated Server Side Events using the Atmosphere Framework.

It is not trivial to broadcast real time server side events using a Comet or WebSocket connection. As an example, if the frequency of your server side events broadcast is high like many events per seconds, it is important to pick up the best strategy when it is time to write those events back to the client. The usual mistake an application does is to sent events to the browser one by one. With the long-polling Comet technique, this gives catastrophic performance issues as the browser needs to reconnect after receiving an events. For the http-streaming Comet’s technique or with WebSocket, the browser doesn’t have to reconnect, but you can still produce catastrophic performance problem on both client and server side if you send events one by one.

With the Atmosphere Framework, you can aggregate or delay events in order to reduce the number of events you sent back to the browser. Let’s explore the first technique, which consist of delaying server side events:

    @Path("/")
    @POST
    @Broadcast(delay=0)
    public Broadcastable buffer(@FormParam("message") String message){
        return broadcast(message);
    }

The code above reflect any form data received from a browser to the set of WebSocket/Comet connections listening to server side events. Instead of automatically sent back the form data to the browser, the code above delay the operation until a second event happens. This is particularly useful if your server side event doesn’t have to be sent real time. Now it may never occurs a second event, so the code above is a little dangerous. Instead, let’s use:

    @Path("/")
    @POST
    @Broadcast(delay=5)
    public Broadcastable buffer(@FormParam("message") String message){
        return broadcast(message);
    }

The difference this time is the server side events will be delayed for 5 seconds maximum. If an second event happens before, the two events will be aggregated and send as a single event.

Although the above mechanism is useful, you may want to aggregate many server side events and send them as a hole instead of one by one. This is particularly true when you need to construct a complex data structure. You can let the browser cache/aggregate the result (but all the data can potentially be lost if the browser crash). Instead, you should create the complex data on the server side by aggregating events. Let’s say your application stream JSON array from an external source. It is quite possible the JSON data received from an external source isn’t fully complete as I’ve demonstrated in my Twitter Feed application, which use the Atmosphere JQuery Plugin (ya, I do what I recommend to avoid :-)):

function subscribe()
   {
     function callback(response)
     {
       $.atmosphere.log('info', ["response.state: " + response.state]);
       $.atmosphere.log('info', ["response.transport: " + response.transport]);
       if (response.transport != 'polling'
         && response.state != 'connected'
         && response.state != 'closed') {

         if (response.status == 200) {
           var data = response.responseBody;

           try {
                var result =  $.parseJSON(incompleteMessage + data);
                incompleteMessage = "";

                 var i = 0;
                 for (i = result.results.length -1 ; i > -1; i--){
                   $('ul').prepend($('').text("["
                         + response.transport + "] "
                         + result.results[i].from_user + " "
                           result.results[i].text));
                 }
            } catch (err) {
               // JSON error (JSON message not complete)
               incompleteMessage = data;
            }
 

In that case, it would be much better to aggregate the server side events until the JSON object has been fully constructed. With Atmosphere, all you need to do is to define a BroadcastFilter. Let’s use a simple example by aggregating String events:

public class StringFilterAggregator implements BroadcastFilter {
    private final int maxBufferedString;
    private final AtomicReference<StringBuilder> bufferedMessage
        = new AtomicReference<StringBuilder>(new StringBuilder());

    public StringFilterAggregator() {
        maxBufferedString = 256;
    }

    public StringFilterAggregator(int maxBufferedString) {
        this.maxBufferedString = maxBufferedString;
    }

    public BroadcastAction filter(Object message) {
        if (message instanceof String) {
            bufferedMessage.get().append(message);
            if (bufferedMessage.get().length() > maxBufferedString) {
                return new BroadcastAction(ACTION.ABORT, message);
            } else {
                message = bufferedMessage.toString();
                bufferedMessage.get().delete(0, bufferedMessage.get().length());
                return new BroadcastAction(ACTION.CONTINUE, message);
            }
        } else {
            return new BroadcastAction(message);
        }
    }
}

All we need to do is to add our Filter to the Atmosphere’s Broadcaster.

    @Path("/")
    @Broadcast
    @POST
    public Broadcastable aggregate(@Context Broadcaster bc) {
         .....
         bc.getBroadcasterConfig().addFilter(new StringFilterAggregator());

        return new Broadcastable(bc);
    }
} 

So now every time an events gets broadcasted, the String will be aggregated until it reach the limit, and only when the limit is reached the write operations will be executed. That can significantly improve the performance of your application.

For any questions or to download Atmosphere Client and Server Framework, go to our main site, use our Nabble forum, follow the team or myself and tweet your questions there! You can also checkout the code on Github.

About these ads
Categories: Atmosphere, Comet, JQuery, Websocket
  1. Brice
    August 5, 2011 at 4:07 pm | #1

    Isnt’ there a typo in the StringFilterAggregator code sample ?

    if (bufferedMessage.get().length() > maxBufferedString) {

    should read
    if (bufferedMessage.get().length() < maxBufferedString) {

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 50 other followers

%d bloggers like this: