Spring WebSocket on RabbitMQ
In this project I was working at, we had websockets implemented over the STOMP protocol. These websockets were currently maintained by our application container. Since we wanted to achieve a multi-instance production environment we would have to use a single message broker so messaging can reach any connected endpoint, not only the ones known by each application container.
This is the second part in the process of externalizing state and responsibilities from application containers. Here you can find the first part and the source code for this demo is on GitHub.
Problem
Our application used websockets to send notifications to connected users. So far, so good. The thing is that we were using our application container message broker implementation and when running multiple application containers, notifications could obviously not reach all intended users because their connection might have been established with any application container, possibly not the with the one sending the message.
Solution
RabbitMQ is a message broker software that can be integrated to the Spring ecosystem. In this scenario our application container receives websockets connections and relay them to RabbitMQ. This way all websockets are knowledgeable by any application container and can get notified.
Source: memorynotfound.com
We will implement this architecture in a demo “mural” app so connected users can post to every connected user to see.
Spring WebSocket
Spring WebSocket is the Spring module that enables WebSocket-style messaging support. As Spring WebSocket’s documentation states, the WebSocket protocol defines an important new capability for web applications: full-duplex, two-way communication between client and server.
Server
Let us start by creating a simple Spring Boot app. You can always go to start.spring.io to bootstrap your app.
Add Spring WebSocket dependency:
We will also want to add the following dependencies to not get a ClassNotFoundException
on reactor.io.codec.Codec
(stick to these specific versions if working with Spring Boot v1.5.x):
And configure the broker relay connection. We do that with the following config class:
In the configureMessageBroker
method, configuration is pretty self-explanatory. We first define application prefix for destinations handled by the application itself. Then follows configuration for the message broker: destination handled, host, port and credentials.
registerStompEndpoints
allows us to register STOMP endpoints over websockets with Sock.js enabled.
We now need a Controller that will map the messages sent from clients to a specific server destination, in this case message
:
@SendTo
will determine that this method’s return will be sent to the specified destination. Destination is topic/mural
here and since it starts with topic, messages here will be sent to the message broker.
Client
We first add dependencies for establishing websocket connections in the client side. These are Sock.js and Stomp Javascript libraries:
Then we create a Javascript controller for websocket connection. It implements several features, such as: connect, disconnect, send messages and subscribe to topics.
We now implement a view that will interact with WebSocketController
and allow us to send and receive messages through a websocket connection:
<head>
section imports required Javascript dependencies that we added to the project.
<body>
presents buttons for triggering websocket connection and disconnection, a text field for message input, a button for sending the message and a <p>
element where received messages will be rendered.
That’s all for the client side configuration.
RabbitMQ
RabbitMQ is a message broker solution which supports multiple messaging protocols. STOMP is one among these supported protocols.
In order to run this demo, we have to install and run RabbitMQ first. It is possible to either download and install it or run its docker image.
As we configured in WebSocketConfig
class RabbitMQ is running with default config properties on localhost:61613
and credentials are guest/guest
.
Play the result
Start our demo app by running SpringWebsocketsRabbitmqDemoApplication
class. Once it has started go to localhost:8080
. Open one more browser tab at the same address.
Click Connect on both tabs and send messages from both sides. The sent messages will be delivered on all connected tabs including the one who sent it. The following screenshot represents a message sent and another received from a second connected tab:
If you have RabbitMQ’s management plugin enabled, you will be able to access localhost:15672
and monitor connections:
Still in RabbitMQ’s management interface you can check Channels and Queues tab. One queue gets created for each connected client, which is represented by a channel. Once a client disconnects both its queue and channel get removed.
In the Exchanges tab there is an exchange named amq.topic. This exchange will be configured by Spring to route messages to queues according to the destination in client subscription we did in our Javascript controller (mural).
Conclusion
Spring WebSocket makes it straightforward to enable websockets and work as a relay to a message broker such as RabbitMQ. This way you may run multiple application container connected to the same instance or maybe a cluster of RabbitMQ instances.
I hope this writing helps someone and feedback is always welcome! :)
References
Besides links throughout this writing, here are some articles that were of help when building this guide:
Post by: Djeison Aguir Selzlein