Talking asynchronous apps

Tutorial: Atmosphere 1.0, WebSocket Portability on the JVM - Part 3

    

 

Fully functional – not just yet!

That’s it, we now have a fully functional chat application. But there are two problems with the current application. The first one is related to Proxy/Firewall. Occasionally Proxy/Firewall doesn’t allow a connection to stay inactive for a longer period of time, and usually the connection gets closed automatically by the Proxy. For a suspended connection, it means the client will have to reconnect every time a connection gets closed. One possible solution is to keep the suspended connection active by sending some bytes between the client and the server. Fortunately for us, all we need to do is to add the HeartbeatInterceptor, which will keep the connection active for us, transparently (Listing 5).

Listing 5: HeartbeatInterceptor

 

  @AtmosphereHandlerService(
          path = "/chat",
          interceptors = {AtmosphereResourceLifecycleInterceptor.class,
                                  BroadcastOnPostAtmosphereInterceptor.class,
                                   HeartbeatInterceptor.class})
  public class ChatRoom extends OnMessage<String> {

 

Now the HeartbeatInterceptor will periodically write bytes (whitespace) to the connection to keep it active. Unfortunately, there are still Proxies that could close the connection after a period of time (active or not) or a network issue could arise and the browser will have to reconnect.

During the process of reconnect, a broadcast operation can always happen and the browser may never get the broadcast because the connection is in the process of connecting. Under that scenario it will mean the browser has missed a message (or lost it). For some applications, it may not be problematic, but for some missing message is a major issue.

Fortunately Atmosphere supports the concept of BroadcasterCache. Installing a BroadcasterCache will allow a Browser to never miss/lose messages. When the Browser reconnects, Atmosphere will always look in the cache and make sure all messages that occurred during the reconnection time are send back to the browser. The BroadcasterCache API is pluggable and Atmosphere ships with ready-to-use implementation. Hence, for our Chat application, all we need to do is:

 

 @AtmosphereHandlerService(
          path = "/chat",
          broadcasterCache = HeaderBroadcasterCache.class,
          interceptors = {AtmosphereResourceLifecycleInterceptor.class,
                                   BroadcastOnPostAtmosphereInterceptor.class,
                                   HeartbeatInterceptor.class})
  public class ChatAtmosphereHandler extends OnMessage<String> {

 

Our application is now guaranteed to never miss or lose a message. The second issue we need to address is intermixed messages, depending on the WebServer used. The Browser may receive two messages in one chunk, one and a half messages, etc. This is problematic because let’s say we use JSON for encoding our message, the Browser will fail to decode messages that takes the form of:

{"message":"Hello World","author":"John Doe","time":1348578675087}{"message":"Cool Man","author":"Foo Bar","time":1348578675087}

or

{"message":"Hello World","author":"John Doe

or

{"message":"Hello World","author":"John Doe","time":1348578675087}{"message":"Cool Man","author"

When the Browser is receiving such messages, it will fail to decode it

var json = jQuery.parseJSON(message);

To solve the issue, we need to install the TrackMessageSizeInterceptor, which will add some hints to the message, and the Browser will be able to use those hints to make sure the atmosphere.js onMessage function is always invoked with a valid message (Listing 6).

 

Listing 6: 

 @AtmosphereHandlerService(
          path = "/chat",
         broadcasterCache = HeaderBroadcasterCache.class,
          interceptors = {AtmosphereResourceLifecycleInterceptor.class,
                                   BroadcastOnPostAtmosphereInterceptor.class,
                                   TrackMessageSizeInterceptor.class,
                                   HeartbeatInterceptor.class})
  public class ChatRoom extends OnMessage<String> {

On the client side, all we need to do is to set the trackMessageLength on the request object.

 

To The Cloud!

We are now ready to deploy our application into the cloud…well, not yet. The next feature we need to add is how messages are getting distributed amongst servers when deployed in a Cloud. The problem we need to solve can be seen in Figure 3.

Figure 3: Servers in the Cloud

 

Under that scenario, when a broadcast action occurs on Tomcat Server 1, Tomcat Server 2 will never get the messages. For our application, that means some users won’t see other messages, which is clearly a major issue. Not only for a chat, but for any application deployed into the Cloud we need to solve that issue.

Fortunately for us, Atmosphere supports “Cloud-Enabled” or “Cluster-enabled” Broadcaster that can be used to propagate message between server instance. Atmosphere currently natively supports well known technologies like Redis PubSub, Hazelcast, JGroups, JMS, XMPP (for example using Gmail servers). For this article, let’s use Redis PubSub (Figure 4).

Figure 4: Redis PubSub

The Redis PubSub allows us to connect to a Redis instance and subscribe to some topics. For our application, all we need to do is to create a ‘chat’ topic and subscribe all our servers to it. Next we just need to tell our application to use the RedisBroadcaster instead of the normal Broadcaster. As simple as Listing 7.

 

Listing 7:

@AtmosphereHandlerService(
        path = "/chat",
        broadcasterCache = HeaderBroadcasterCache.class,
        broadcaster = RedisBroadcaster.class,
        interceptors = {AtmosphereResourceLifecycleInterceptor.class,
                                 BroadcastOnPostAtmosphereInterceptor.class,
                                 TrackMessageSizeInterceptor.class,
                                 HeartbeatInterceptor.class})
public class ChatRoom extends OnMessage<String> {

 By just adding the RedisBroadcaster we just enabled message sharing between servers, making our chat application “Cloud-aware” in a single line. On the client side we don’t have to change anything. We now have a fully functional application:

  • Transparently supporting all existing WebServers

  • Transparently supporting all existing Browsers

  • Cloud/Cluster enabled

Our application will first negotiate the best transport to use between the client and the server. For example, assuming we deploy using Jetty 8, the following transport will be used 

  • Chrome 21 : WebSockets

  • Internet Explorer 9 : Long-Polling

  • FireFox 15: Server Side Events

  • Safari/iOS 6: WebSockets

  • Internet Explorer 10: WebSockets

  • Android 2.3: Long-Polling

  • FireFox 3.5 : Long-Polling

All of this transparently allows a developer to focus on the application instead of transport/portability issues.

Conclusions and Considerations

WebSockets and Server Sides Events are technologies on the rise and their adoption within the enterprise is accelerating. Some things to think about before jumping in:

  • Is the API portable, e.g. will it work on all well-known WebServer?
  • Is the framework already offering a transport fallback mechanism? For example, Internet Explorer 7/8/9 neither support WebSockets and Server Side Events, and unfortunately for us, those browsers are still widely used.
  • Is the framework cloud enabled, and more important, will it scale?
  • Is it easy to write application, is the framework well established?

Clearly, the Atmosphere Framework is the response for those four really important questions. Still having doubts? Well, go to the Wall Street Journal, open pages and look for the Wordnik's logo. More than 60 million requests per day, and all of this powered by the Atmosphere Framework! Start today by going to our website!

Author Bio: 

Jeanfrançois has been working in software engineering for the last 18 years. He studied pure mathematics and worked for a Canadian research centre, doing mathematical modeling in C++ until someone introduced him to a new language called Java. He never stopped using it. Jeanfrançois worked for Sun Microsystems for almost 10 years, before writing one of the first NIO frameworks, Grizzly, Jeanfrançois also developed the Grizzly Comet Framework, which was an early way to implement asynchronous web applications. He then started the Atmosphere Framework, which brings portability across Servlet container and allows the creation of WebSocket and Comet applications. He can be followed on twitter at http://twitter.com/jfarcand

This article appears in JAX Magazine: Atmosphere 1.0 from October. For that and previous back issues, click here.

Flickr Image courtesy of ell brown

Jeanfrancois Arcand
Jeanfrancois Arcand

What do you think?

JAX Magazine - 2014 - 03 Exclucively for iPad users JAX Magazine on Android

Comments

Latest opinions