WebSocket Architecture in Spring Framework 4.0

header-graphic-spring-STOMP-WebSocketTwo years ago, the WebSocket protocol RFC 6455 defined an important new capability for web applications: full-duplex, two-way communication between client and server. Part of the “HTML5” umbrella term, WebSocket has gathered a lot of attention and become an official buzzword—one that generates plenty of expectations, in many cases, ahead of actual experience. In this post, we will focus on separating hype from reality and introduce the Spring Framework 4.0 approach to building WebSocket-style applications using STOMP as an application-level protocol, SockJS for fallback options, and a message broker for broadcasting messages to connected users.

Considering the long list of techniques used to make the web more interactive, including Java applets, XMLHttpRequest, Adobe Flash, ActiveXObject, various Comet techniques, and server-sent events, WebSocket is an exciting, new capability. However, the truth is that WebSocket is more of a foundation. It does something very important by setting a standard for two-way communication over the web, but it’s really only the first stop. Aside from short-to-midterm challenges with network proxy configuration and browser support, WebSocket opens the door to additional architecture questions for development teams to answer.

WebSocket vs REST

To get a sense of the kinds of questions you might run into when you start thinking about a WebSocket application, have a quick look through this InfoQ post summarizing a discussion around whether WebSocket will replace REST. The degree to which the discussion seems amusing, outrageous, or plausible will vary on the type of application. Some applications have a stronger motivation for always-on, real-time interaction (e.g. games, finance, collaboration, visualization, etc) than occasional notifications (web email, news updates, etc). Either way, it’s human to relate to something familiar, and REST is how we build web applications today. So rather than resisting the idea of such a comparison, let’s go ahead and see what we can learn from it. There are two key observations to make.

First, REST is an architecture style that encourages many nouns (URLs) and a handful of verbs (HTTP methods) while remaining stateless and using hypermedia (links) among others. WebSocket by comparison points to an entirely different, messaging-style architecture. It is not merely a replacement for existing Ajax techniques and requires an event-driven, reactive approach.

Second, REST is built on HTTP, an application-level protocol on top of TCP that gives us the URLs, HTTP methods, headers, status codes, and other crucial pieces that we need to build application logic on. WebSocket, by comparison, is a thin layer over TCP. It is not much more than a stream of bytes broken down into messages with undefined content. There is very little a framework can do without making assumptions about the content within a message. When applications make such assumptions, they will have to create their own framework around those assumptions.

The WebSocket protocol does define the use of sub-protocols (aka higher-level protocols) but does not require it. Either way, an application will need to decide what message format to use—custom, framework specific, or standard.

In short, a WebSocket-style application implies an event-driven, reactive messaging architecture. Furthermore working on the WebSocket level is arguably too low level for most applications just like most web applications today aren’t programming directly to sockets.

spring-13-800x300px-final

Approaches to the Real-time Web

We can look to a number of existing frameworks with higher level messaging APIs that use WebSocket underneath and may also rely on other fallback options (e.g. HTTP streaming, long polling, etc) when necessary. Reliance on a mix of WebSocket and non-WebSocket techniques is still necessary even today. The key difference is that frameworks can provide a single API to use while transparently falling back on non-WebSocket transports if needed.

Some frameworks, like Socket.io and Vert.x, provide lightweight application facilities for responding to events and routing messages to specific handlers. Others, like CometD, provide channels for subscribing and receiving messages along with a built-in lightweight message broker. It is also an option to connect to real message brokers such as RabbitMQ or ActiveMQ directly from a browser. When it comes to messaging architectures, message brokers are suited to the role and built to scale.

The Spring Framework 4.0 Approach

One of the goals of Spring Framework 4.0—currently a release candidate, scheduled for GA in December 2013—is to provide support for WebSocket-style applications. It goes well beyond simply offering a WebSocket API on top of JSR-356 containers and also provides fallback options for use in browsers and networks that don’t support or allow use of WebSocket. More importantly, it provides a foundation for building WebSocket-style messaging architectures for use in web applications.

For fallback options we decided to use the SockJS protocol. It has the best and widest range of transports for fallback options.

For the WebSocket-style messaging architecture, we looked at many existing approaches, and we liked both the power of a real message broker as well as the feel of a web application centric programming model. After all, we need to adopt a messaging architecture, but we are also web developers used to building web application so the result shouldn’t be too different from what we know.

The first step was choosing a message format. A number of simple messaging protocols exist including STOMP, MQTT, and WAMP. These are all suitable for use in web clients and provide support for basic messaging patterns. We picked STOMP because the message format is modeled on HTTP and because it is widely supported. However, our programming model does not have a strong bias to STOMP and can be extended to support other similar protocols.

Using STOMP puts us at a level above WebSocket. It gives us a way to express who a message is for and what messages we are interested in receiving. It allows us to use available client-side libraries such as stomp.js and msgs.js, as well as plug in a real message broker for broadcasting. These are significant gains.

Spring Framework 4 provides STOMP support. With 2-3 lines of configuration, you can enable it to act as a lightweight message broker to web clients. It will automatically handle subscriptions without any server code and allow controller methods to handle incoming messages and subscriptions. This is similar to how Spring MVC maps HTTP requests to controller methods. In fact, a Spring MVC controller can be extended to also receive STOMP over WebSocket messages:

@Controller
public class GreetingController {

@RequestMapping(value=”/greeting”, method=POST)
public void httpGreet(String text) {

// ...
}

@MessageMapping("/greeting")
public void stompGreet(String text) {

// ...
}

}

Using a Full-Featured Message Broker

It is also easy to plug in a full-featured message broker. For example, RabbitMQ (or any other STOMP message broker) can be used for subscriptions for broadcasting messages to clients. In this scenario, Spring is still the web application layer that web clients connect and talk to. However, it also serves as a gateway to RabbitMQ, allowing messages from the application to flow to RabbitMQ and then back down to subscribed clients. The following diagram illustrates the approach:

Full-Featured-Message-Broker

This approach extends to multi-server and cloud environments where any number of application instances can broadcast through a RabbitMQ service and reach all connected clients regardless of which application instance they happen to be connected to. Furthermore, it’s easy to broadcast messages to connected clients from HTTP request-handling methods (i.e. REST API) or any other part of the application.

For a more detailed technical overview, see the M2 blog post on spring.io, run the Stock Portfolio sample, or watch the webinar on the Spring Dev channel. We have recently released an RC1 candidate. If you have an application in mind, now is the perfect time to try it out and provide feedback.

If you happen to be in the London area or with easy access to it, check out the Spring eXchange—a packed two-day show on Nov 14/15, with key Spring engineers presenting the latest and greatest including Spring Framework 4.0 and our STOMP over WebSocket support!

Permalink

Tags: , , ,

14 comments on “WebSocket Architecture in Spring Framework 4.0

  1. Changsu Jiang on said:

    Hi Rossen,

    Thank you for the great work! I am not sure if this is a good platform, but I have a question running the portfolio sample application with external mq servers (both activemq and rabbitmq). I can get the app to work for only several seconds, then the connection is closed. The part of the log is like:

    16:39:32 [qtp262173721-30] DefaultSockJsService – Creating new session with session id “f3vp1xwk”
    16:39:32 [qtp262173721-30] DefaultHandshakeHandler – Initiating handshake for http://localhost:8080/spring-websocket-portfolio/portfolio/860/f3vp1xwk/websocket, headers={Accept-Language=[en-US,en;q=0.5], Sec-WebSocket-Version=[13], Cookie=[JSESSIONID=vvqalej8x04isj75im6wa5mj], Upgrade=[websocket], Host=[localhost:8080], Accept-Encoding=[gzip, deflate], User-Agent=[Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:23.0) Gecko/20100101 Firefox/23.0], Origin=[http://localhost:8080], Sec-WebSocket-Key=[sM8HfMvRoNyt6NbH4xrHnQ==], Connection=[keep-alive, Upgrade], Accept=, Cache-Control=[no-cache], Pragma=[no-cache]}
    16:39:32 [qtp262173721-30] DefaultHandshakeHandler – Requested version=13, supported=[13]
    16:39:32 [qtp262173721-30] DefaultHandshakeHandler – Requested sub-protocol(s): [], supported sub-protocol(s): []
    16:39:32 [qtp262173721-30] DefaultHandshakeHandler – Requested extension(s): [], supported extension(s): [identity, fragment]
    16:39:32 [qtp262173721-30] DefaultHandshakeHandler – Upgrading request, sub-protocol=null, extensions=[]
    16:39:32 [qtp262173721-30] DispatcherServlet – Null ModelAndView returned to DispatcherServlet with name ‘dispatcher’: assuming HandlerAdapter completed request handling
    16:39:32 [qtp262173721-30] DispatcherServlet – Successfully completed request
    16:39:32 [qtp262173721-30] LoggingWebSocketHandlerDecorator – Connection established, SockJS session id=f3vp1xwk, uri=ws://localhost:8080/spring-websocket-portfolio/portfolio/860/f3vp1xwk/websocket
    16:39:32 [qtp262173721-49] LoggingWebSocketHandlerDecorator – TextMessage payload= CONNECT
    ac.., length=56, last=true], SockJS session id=f3vp1xwk
    16:39:32 [qtp262173721-48] LoggingWebSocketHandlerDecorator – TextMessage payload= SUBSCRIBE
    .., length=48, last=true], SockJS session id=f3vp1xwk
    16:39:32 [qtp262173721-48] LoggingWebSocketHandlerDecorator – TextMessage payload= SUBSCRIBE
    .., length=54, last=true], SockJS session id=f3vp1xwk
    16:39:32 [qtp262173721-48] LoggingWebSocketHandlerDecorator – TextMessage payload= SUBSCRIBE
    .., length=65, last=true], SockJS session id=f3vp1xwk
    16:39:32 [qtp262173721-48] LoggingWebSocketHandlerDecorator – TextMessage payload= SUBSCRIBE
    .., length=55, last=true], SockJS session id=f3vp1xwk
    16:39:32 [BrokerWebSocketChannel-1] PortfolioController – Positions for fabrice
    16:39:40 [BrokerWebSocketChannel-1] WebSocketServerSockJsSession – Closing SockJS session id=f3vp1xwk, CloseStatus
    16:39:40 [BrokerWebSocketChannel-1] JettyWebSocketSession – Closing WebSocket session id=4ea59b72
    16:39:40 [qtp262173721-45] WebSocketServerSockJsSession – SockJS session id=f3vp1xwk was closed, CloseStatus
    16:39:40 [qtp262173721-45] LoggingWebSocketHandlerDecorator – Connection closed for SockJS session id=f3vp1xwk, CloseStatus
    16:39:40 [BrokerWebSocketChannel-1] LoggingWebSocketHandlerDecorator – Connection closed for SockJS session id=f3vp1xwk, CloseStatus

  2. WebSocket is cool and I have used it in my application a couple of years ago. The issue I faced was the implementation is native to Jetty, thus it is not portable to another container. With this websocket architecture in Spring 4.0, there is a chance it can be portable now?

    • Rossen Stoyanchev on said:

      Thanks for your comment. Our STOMP over WebSocket support is indeed portable. At present it has been tested on Tomcat 7 and 8, Jetty 9+, and Glassfish 4.

  3. Ondrej on said:

    Hi,
    Is there any Java library which can be used for accessing STOMP over Websocket? For example if you want to write a test in java which will open a websocket connection and will be able to subscribe, receive and send messages? All client libraries you suggested are for javascript.

  4. Daniel on said:

    Hello,

    I’ve tried the portfolio sample, and it works on chrome and firefox. But when trying with ie9, I get STOMP protocol error Whoops! Lost connection to undefined. Is the fallback supposed to work here? I see but are no longer in the master.

    • Rossen Stoyanchev on said:

      I just tried running with IE 9 and didn’t encounter have any issues. Since the sample is using 4.0.1 snapshots, it may help to force and update with “mvn -U”. Other than that I would recommend setting the log level to TRACE and check what happens on the server side.

      • Same here with me. Doesnt work with IE9. Also tried mvn -U and it didnt find any updates. So does this mean SockJs isnt fully supported in IE9. BTW the same page loads fine on Chrome.

        • Rossen Stoyanchev on said:

          I would love to see some details – Spring Framework version used (you should be on 4.0.1 at this point), any kind of client-side error messages (from the browser or the WebSocket connection in the network tab), server side log information with TRACE enabled for org.springframework.messaging and org.springframework.web. Would you mind creating a ticket in JIRA? This isn’t the right medium for getting through issues.

  5. Why you didnt not use bayeux (cometd)? what are the benefits of stomp?

    • Rossen Stoyanchev on said:

      Using STOMP makes it easy to plug in a full-featured message broker (e.g. RabbitMQ, Apollo, ActiveMQ) for broadcasting.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>