WebSockets with Grails

Last week I had an interesting discussion about how to relief a threaded application server’s burden. If clients regularly poll the application server with a certain time window, just to ask whether there are updated information available, every time a server thread is opened. With many clients connecting simultaneously to the server the server’s thread pool can be exhausted, causing a halt to the application.

While there are non-blocking servers on the market, often you cannot easily switch your server product or change the overall application architecture to a message driven architecture.

Therefore, one possible architectural solution we discussed, was the concept of WebSockets.

The HTML5 Websocket specification defines a full duplex two-way communication between client and server over a single connection. Two-way communication means, that with WebSockets a server is able to push a message out to its clients. In consequence, a given threaded server can serve more clients or support more concurrent connections.

Don’t be misleaded by the word WebSocket. WebSocket is not a socket technique like you might know from your unix system, WebSocket is a protocol. The WebSocket starts as an ordinary http connection (http://), but then asking the server that it want to switch to the WebSocket protocol (ws://). If this request is granted, the http connection breaks down and is replaced by a WebSocket connection over the same underlying TCP/IP connection. The WebSocket uses port 80 or 443. Now, the communication packets can be sent  back and forth between client and server. They even travel through proxies and firewalls, which eases the application development a lot.

Let us see, how we can make use of WebSockets with Grails.

This demonstrator sends the server’s cpu load to a client’s web browser. On the server side exists a service SimpleMessagingService, which measures the cpu load every 5 seconds. The result is put into a topic queue “topic/cpuLoad”. Over the WebSocket protocol the queue’s content is pushed out to the browsers of all clients, who are subscribed to this topic queue. All subscribed browsers are updated all most simultaneously.

WebSocketBrowser

A very nice WebSocket plugin exist for Grails. It makes the integration of Spring 4.0 WebSocket support a breeze. Just add compile “:spring-websocket:1.3.0” to the plugins section of Grails BuildConfig.groovy.

With Spring 4.0 WebSocket there are various possibilities to send or receive message. Here I will use the SimpMessagingTemplate and its convertAndSend function.

SimpleMessagingService.groovy

package com.jolorenz

import org.springframework.messaging.simp.SimpMessagingTemplate
import java.lang.management.ManagementFactory;

import com.sun.management.OperatingSystemMXBean;
import grails.transaction.Transactional
import groovy.json.JsonBuilder

@Transactional
class SimpleMessageService {

   SimpMessagingTemplate brokerMessagingTemplate
   OperatingSystemMXBean bean = (com.sun.management.OperatingSystemMXBean)   
   ManagementFactory.getOperatingSystemMXBean()

   void systemCpuLoad() {

      def cpuLoad = bean.getSystemCpuLoad()
      cpuLoad = (cpuLoad*100).round(2)

      def builder = new JsonBuilder()
      builder {
         message("CPU system load: " + cpuLoad + "%")
         timestamp(new Date())
      }
      def msg = builder.toString()
      brokerMessagingTemplate.convertAndSend "/topic/cpuLoad", msg
   }
}

Next, we need a html/javascript page with the corresponding topic queue, e.g. “/topic/cpuLoad”.

index.gsp

<!DOCTYPE html>;
<html>;
   <head>;
   <meta name="layout" content="main" />
   <asset:stylesheet src="application.css" />
   <asset:javascript src="application.js" />
   <asset:javascript src="jquery" />
   <asset:javascript src="spring-websocket" />

   <script type="text/javascript">
      $(function() { 
         //Create a new SockJS socket - this is what connects to the server
         //(preferably using WebSockets).
         var socket = new SockJS("${createLink(uri: '/stomp')}");

         //Build a Stomp client to send messages over the socket we built.
         var client = Stomp.over(socket);

         //Have SockJS connect to the server.
         client.connect({}, function() {             
            client.subscribe("/topic/cpuLoad", function(message) {
               var msg = JSON.parse(message.body)
               var time = '<strong>;' + new Date(msg.timestamp).toLocaleTimeString() + '</strong>'
               $("#cpuLoadDiv").append(time + ': ' + msg.message + "<br/>");
            });
         });                
      });
   </script> 
</head>

<body>
   <div id="cpuLoadDiv"></div>
</body>
</html>

Indeed, this is all what is needed to establish a WebSocket communication between server and client. Yes, it is a simple example and lacks features like filters for user groups or security, but it gives you an idea to become familiar with WebSockets.

What are the Cons?
WebSocket is a browser feature and not all browsers support this feature. Probably in the near future, all browsers and servers will support WebSockets.
CanIUse provides a good overview of the supported web browsers.

Browser with WebSocket Support

WebSocketSupport

WebSockets are a suitable technique, but what if the browsers of my audience do not support it yet?
Just in case, falling back to “Long Polling” is a workaround to get effectively the same behavior.
Long Polling is not as efficient but it works nicely. Bear in mind, that your web frameworks should gracefully fallback to use Long Polling when Web Socket is not available.

HTH Johannes

Related readings:

Advertisements
This entry was posted in Development and tagged , , . Bookmark the permalink.

2 Responses to WebSockets with Grails

  1. Pingback: Diario di Grails (settimana 12 del 2016) | BME

  2. cfsilence says:

    Can you please clarify how the message is sent every 5 seconds? I’m not seeing anything in the code that would trigger that.

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