Archive

Posts Tagged ‘atmosphere comet websocket redis jqeury pubsub’

Scaling your Websocket/Comet Real-time application using Redis Pub/Sub

October 25, 2010 3 comments

Scaling Websocket/Comet applications are not simple. Why? As with Comet, a websocket connection on server A cannot receives or share events with another websocket connection on server B.  Just think about an application deployed in the cloud. the same server doesn’t serve all connections, you need some communications between all the servers. You can write your custom mechanism and ends up with kilometers of code, or use the Atmosphere Framework to do it for you.

Atmosphere supports many communication channel between server, but today we will focus on our newly addition: Redis. Using Redis pub/sub API,  we will show how it is possible to deploy a WebSocket application “into the cloud”. Here I will use Atmosphere JQuery Plugin on the client side as the plug in will always try to use WebSocket, and if fail will fall back to Comet techniques. And fortunately, the Plug In will also fall back to Comet as well if the remote server isn’t supporting WebSocket.  This is great because when you deploy into a cloud like EC2, you aren’t guarantee all servers will be the same product, same version etc. Less things to worries.

OK, so let’s write a simple PubSub application. Without going into the details (read this entry for an introduction of Atmosphere JQuery Plugin), our client side will looks like (browse the code here):


    function callback(response)
    {
      if (response.status == 200) {
        // display the result.
      }
    }
    /* transport can be : long-polling, streaming or websocket */
    $.atmosphere.subscribe(uri + 'pubsub/' + "A topic",
                           callback,
                           $.atmosphere.request = {transport: 'websocket'});

As simple as it look, we just invoke  the subscribe method with a uri, a callback and the default transport we want to use: WebSocket. The server will understand the request and act appropriately: upgrade for WebSocket, avoid committing the response for Comet. For publishing, you can use any existing Redis client or use Atmosphere JQuery Plugin as well

    $.atmosphere.response.push(uri + 'pubsub/' + "A Topic",
                               $.atmosphere.request = {data: 'Some Message'});

Here the push method re-use the existing connection (the WebSocket one) to push messages back to the server.  If WebSocket is not supported, the Plug In will still works and fall back to use Comet. That’s it for the client side.

Now on the server side, let’s use the Atmosphere’s Jersey module and the Atmosphere Redis Plugin. The Redis Plugin uses the great Jedis Client internally to connect to Redis (browse the code here)

    private @PathParam("topic") RedisBroadcaster topic;

    @GET
    public SuspendResponse subscribe() {

        return new SuspendResponse.SuspendResponseBuilder()
                .broadcaster(topic)
                .outputComments(true)
                .addListener(new EventsLogger())
                .build();
    }

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

Don’t look for more code, there isn’t more! How it works:

  1. When the JQuery Plugin issue a request like GET /pubsub/”A Topic”, the above resource is created and an instance of RedisBroadcaster gets injected.  The RedisBroadcaster usesthe value of “A Topic” for subscribing to redis’s server queue. “A Topic”  is also the name of the RedisBroadcaster, which can always be used to retrieve it.
  2. Second, the subscribe method gets invoked, and we tell Atmosphere to suspend the response. For WebSocket, it means accept the upgrade handshake.
  3. Now any messages send using either a Redis client (like redis-cli) or Atmosphere JQuery’s $.atmosphere.response.push will transit into Redis’s pubsub API. The $.atmosphere.response.push will invoke the publish method of your resource, which will use the RedisBroadcaster to progate the change to all WebSocket/Comet connection via Jedis/Redis.

If you are new to Redis, the following explain how to set it up (taken from this great blog on Comet+Atamosphere+Akka)

./redis-cli
redis> subscribe atmosphere
Reading messages... (press Ctrl+c to quit)
1. “subscribe”
2. “atmosphere”
3. (integer) 1

redis-cli is a simple Redis console client. subscribe default starts waiting for messages published to the channel default in the foreground. Redis response confirms that the client was subscribed to the channel. Now, lets post something to the channel:

./redis-cli
redis> publish atmosphere Hello!
(integer) 1

If you have deployed the sample described in this blog, the Hello! message will be pushed back to your browser. Now you may have noticed that every time we publish a message, the message will transit from your application, to the Redis server, back to your application (take a look at the RedisBroadcaster internal if you are curious). You may not have to do this round trip if you use a single server. Instead, you can use the RedisFilter with the  normal Broadcaster:

    private @PathParam("topic") Broadcaster topic;

    @GET
    public SuspendResponse subscribe() {

        topic.getBroadcasterConfig().addFilter(new RedisFilter());

        return new SuspendResponse.SuspendResponseBuilder()
                .broadcaster(topic)
                .outputComments(true)
                .addListener(new EventsLogger())
                .build();
    }

With the Redis is every time you publish a message, the normal Atmosphere Broadcaster will first publish it locally (using a simple in-memory queue), and then publish it to Redis without re-publishing the result it will get from Redis. In some applications it may be an optimization to use the in-memory queue.

As demonstrated, writing an application with Atmosphere is super simple, and deploying it inside a cloud/distributed environment is also simple. The good news here is you don’t have to focus on hwo redis work, but on your application. On a side note, if you want to use ActiveMQ (any JMS impl) or JGroups intead of Redis, this is as simple as:

    private @PathParam("topic") JMSBroadcaster topic;

    @GET
    public SuspendResponse subscribe() {

        return new SuspendResponse.SuspendResponseBuilder()
                .broadcaster(topic)
                .outputComments(true)
                .addListener(new EventsLogger())
                .build();
    }
    private @PathParam("topic") JGroupsBroadcaster topic;

    @GET
    public SuspendResponse subscribe() {

        return new SuspendResponse.SuspendResponseBuilder()
                .broadcaster(topic)
                .outputComments(true)
                .addListener(new EventsLogger())
                .build();
    }

Just need to inject the proper technology you want to use.

The complete binary/source code can be downloaded here. For any questions or to download Atmosphere Client and Server Framework, go to our main site and use our Nabble forum, or follow the team and myself and tweet your questions there!

Follow

Get every new post delivered to your Inbox.

Join 51 other followers