Relay Websocket connection using RabbitMQ in Spring Websocket

A problem for applications that use Websocket is when deploying these applications with many different instances, handling requests from users with load balancer, the functionality of the application still works normally, the user still connects to the application and receives messages through Websocket without any problem. Because if you deploy multiple instances of the application, if the user connects to this instance, the other instance of the application will not be able to send messages to that user. To solve this problem, you can use a message broker like RabbitMQ to relay the Websocket connection. How is it in detail? If your application uses Spring Websocket, let’s learn together in this tutorial!

I will use the example application in the tutorial Implement WebSocket with Spring framework, deploy this application to many different containers and use Nginx as load balancer to see before using RabbitMQ replay Websocket connection, what will happen!

First, I will build a Docker Image for Nginx, the content of Nginx’s Dockerfile is as follows:

with the nginx.conf file has the following initial content:

Different from nginx.conf file configuration in tutorial Deploy load balancer application using Nginx with Docker, in the example of this tutorial, we are working with Websocket connection, we need to have configuration related to upgrading HTTP connection to Websocket connection.

When Nginx forwards Websocket connection request from a client to the upstream server, it needs to add Upgrade and Connection headers to establish Websocket connection. You can read more about the Upgrade header here.

The value of the Upgrade header will be taken from the $http_upgrade variable, the value of this $http_upgrade variable will be obtained by Nginx from the Upgrade header in the request from the client.

The value of the Connection header will be obtained from the $connection_upgrade variable. The definition of this $connection_upgrade variable uses Nginx’s map module as you can see:

The default value of the $connection_upgrade variable is always Upgrade, in case the value of the Upgrade header from the client request is empty, its value will be “close”.

I will deploy the example application to 3 different containers, but to show you the problem when deploying a Websocket application with a load balancer, I only configure it to have Nginx forward requests to 1 container first. In this case, we won’t need to relay Websocket connection with a message broker like RabbitMQ.

To build Docker Image for Nginx, I will run the following command:

Result:

The content of the Dockerfile to build the Docker Image for the example application is as follows:

To build Docker Image for this example application, remember to build the application with the command “mvn clean install” first, then run the following command:

Result:

Our Docker Compose will have the following content:

Run the command “docker compose up” in the directory containing this docker-compose.yaml file, you will see the following results:

Now if you open 2 browser windows and go to http://localhost, enter your name in the box “What is your name?” and then press the Send button, you will see that the application works normally as I did in the previous tutorial, my results are as follows:

If you now edit the nginx.conf file to add 2 other containers of the application, for example:

after that:

  • Stop Docker Compose,
  • Delete the application deployed with Docker Compose,
  • Delete the Nginx Docker Image we just built above,
  • Rebuild Docker Image for Nginx,
  • Run Docker Compose again,
  • Done go to the address http://localhost,
  • Enter your name in the “What is your name?” box.
  • Then press the Send button,

You will see that when I send a message in this browser window, the other window does not receive messages anymore:

Now, we will configure the example application’s Websocket so that it relays the Websocket connection using RabbitMQ.

First, we will need a RabbitMQ server.

I showed you how to install RabbitMQ server using Docker Compose. The RabbitMQ Docker Image in this tutorial has not been enabled to support STOMP messages, the protocol that our example application is using for the client and server to communicate with each other, so I will build a new Docker Image for RabbitMQ with enabling the STOMP plugin. The content of the RabbitMQ server’s Dockerfile will look like this:

I will build Docker Image for RabbitMQ with the above Dockerfile with the following command:

Now we can declare a new service for RabbitMQ in our docker-compose.yaml file as follows:

By default, the STOMP Plugin will listen on port 61613 and has a default for system login with username/passcode as guest/guest respectively.

Now we will modify the code of the WebSocketConfiguration class in the example application to use StompBrokerRelay with RabbitMQ instead of SimpleBroker, like so:

Hey, I’m using the default connection information to RabbitMQ like host is “localhost”, port is “61613”, system user with passport is “guest” and “guest” respectively. If the connection information is different from the default values, you can add the following code:

I will deploy the application with Docker Compose, so the value of my host is set to 172.17.0.1 as you can see.

For RabbitMQ to work, you also need to add the following dependencies:

Now, please build and run the application, then open 2 browser windows and do the same operation again, you will also see that both browsers receive messages from the WebSocket server.

Add Comment