Archive

Archive for June, 2011

REST + WebSocket applications? Why not using the Atmosphere Framework

The Atmosphere Framework easily allow the creation of REST applications … using WebSocket. This time I will describe a super simple example on how to do it.

The Atmosphere Framework supports transparently both WebSocket and Comet transport and brings portability to any Java based application. An application written using Atmosphere can be deployed in any WebServer and Atmosphere will transparently make it work.  Atmosphere is also able to transparently select the best transport to use, e.g. WebSocket or Comet. Now let’s write a very simple REST application with Comet support as we normally write:

@Path("/pubsub/{topic}")
@Produces("text/html;charset=ISO-8859-1")
public class JQueryPubSub {

    private @PathParam("topic") Broadcaster topic;

    @GET
    public SuspendResponse<String> subscribe() {
        return new SuspendResponse.SuspendResponseBuilder<String>()
                .broadcaster(topic)
                .outputComments(true)
                .addListener(new EventsLogger())
                .build();
    }

    @POST
    @Broadcast
    public Broadcastable publish(@FormParam("message") String message) {
        return new Broadcastable(message, "", topic);
    }
}

Doing

GET /pubsub/something

will invoked the SuspendResponse. To make the exercise simple, all we do there is suspend the connection (e.g. do not return any response, wait for an event). If you want to make this exercise more difficult, you can always implements the ETag trick! Once the connection is suspended, we need to use a second connection in order to post some data

POST /pubsub/something
message=I love Comet

Executing the POST request will result in the invocation of the publish method. The @Broadcast annotation means the FormParam value will be broadcasted to all suspended connections.

WebSocket to rule them all

OK so let’s assume we now deploy your application in a WebServer that supports WebSocket like Jetty or GlassFish. Now Atmosphere will auto detect WebSockets are supported and use it when a WebSocket request is done. Now let’s assume we build the client using the Atmosphere JQuery Plug In and execute the GET request using Chrome (which support Websocket).The Atmosphere Javascript library is able to challenge the remote server and discover if the server and client support WebSocket, and use it.

In that scenario, suspending the connection will tell Atmosphere to execute the WebSocket handshake. Now the POST will be executed on the same connection, and the public method will be invoked this time.  This is not a POST as you see normally with normal HTTP. Since WebSocket is used, only the form param will be send over the wire:

message=I love WebSocket

All of this occurs without any modification of your REST application. All you need to do to enable WebSocket is to “suspend” the connection when a @GET occurs. Transparent, is it :-) You can download the current version of the sample here.

Now what Atmosphere is doing under the hood is wrapping the WebSocket message into an HttpServletRequest so any framework like Jersey, Wicket, etc, works as it is. If you are familiar with Atmosphere, your AtmosphereHandler implementation will get invoked with an instance of HttpServletRequest that contains the WebSocket message, so you can use it as your will normally do using Comet or normal HTTP request.

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.

Categories: Atmosphere, Comet, JQuery, Websocket

Improving HTTP Long Polling performance using ETag

The Comet technique called “long polling” is the most widely used technique. In this blog I will explain a way optimize this technique using the well know etag HTTP header using the Atmosphere Framework.

There are many ways to optimally implement the long polling technique, and how events get cached on the server side. In most frameworks/applications, events are getting cached to prevent clients from missing them when reconnecting. When the client reconnect, missed events are retrieved using custom techniques like:

  • Use a special header containing the last time the client has connected. Based on that time stamp, the server can retrieve all events that occurred after that time.
  • Use a special header container an event counter, and retrieve the missed events based on that count

A better approach consist of using the well know Etag HTTP header. Wikipedia defines Etag as:

An ETag, or entity tag, is part of HTTP, the protocol for the World Wide Web. It is one of several mechanisms that HTTP provides for cache validation, and which allows a client to make conditional requests. This allows caches to be more efficient, and saves bandwidth, as a web server does not need to send a full response if the content has not changed. .

That’s exactly what we need to optimize our long polling request, e.g use an Etag to:

  1. Decide when to long poll the connection
  2. Decide to long poll the connection even if some events where cached since the last connection. Aggreate the cached events with the new one.
  3. Avoid long polling the connection, return immediately.

Before going into the details, let’s describe how an ETag can be generated from the server side. A simple but very powerful way to generate an Etag is to generate it based on a MD5 Message-Digest Algorithm. That value will be regenerated every time events get cached, and that value will be used as an Etag send back to the client.

The client will use the ETag value and send it back to the server using the “If-None-Match” header. On the server side, the value will be compared with the last generated ETag value. If the client’s ETag match the server one, that means no event occurred since the last time the client was connected. Two actions can be taken:

  • Long poll (suspend) the connection until the ETag gets regenerated, which means a new events occurred on the server side.
  • Return an HTTP status code of 204: The server successfully processed the request, but is not returning any content. Returning a 204 could be used when the number of long polled connections is really high to prevent out of memory errors or to reduce the memory footprint. In that case the client could reconnect later (polling the server).

If the ETag values aren’t matching, that mean events occurred since the last time the client connected. Two actions can be taken:

  • Sent back the cached events to the client. In that case we don’t long poll the connection.
  • long poll the connection (suspend) until the next events occurs. Then combine the cached events with the new one and resume the connection.

Note that every time a new events occurs, a new ETag value needs to be regenerated.

Now how can this be implemented? Using the Atmosphere Framework it is as simple as:

@Produces("application/json")
public SuspendResponse optimalLongPolling(final @Context Request request){
    final String eTagString = server.getETag();
    final Response.ResponseBuilder responseBuilder =
        request.evaluatePreconditions(new EntityTag(eTagString));

    if (responseBuilder != null){
        return new SuspendResponse.SuspendResponseBuilder()
                .broadcaster(broadcaster)
                .addListener(new AtmosphereEventsListener())
                .outputComments(false)
                .resumeOnBroadcast(true)
                .period(30, TimeUnit.MILLISECONDS)
                .build();
    } else {
        throw new WebApplicationException(
                Response.status(Response.Status.NO_CONTENT).build());
    }
}

Very simple.

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.

Categories: Atmosphere
Follow

Get every new post delivered to your Inbox.

Join 51 other followers