Relay Websocket connection sử dụng RabbitMQ trong Spring Websocket

Một vấn đề đặt ra cho các ứng dụng có sử dụng Websocket là làm sao khi deploy các ứng dụng này với nhiều instance khác nhau, handle request từ người dùng bằng load balancer, functionality của ứng dụng vẫn hoạt động bình thường, user vẫn connect tới ứng dụng và nhận message thông qua Websocket mà không có vấn đề gì. Vì nếu deploy nhiều instance cho ứng dụng, nếu user connect tới instance này, instance khác của ứng dụng sẽ không thể gửi message tới user đó. Để giải quyết vấn đề này, các bạn có thể sử dụng một message broker như RabbitMQ để relay Websocket connection. Cụ thể như thế nào? Nếu ứng dụng của các bạn sử dụng Spring Websocket thì hãy cùng nhau tìm hiểu trong bài viết này các bạn nhé!

Mình sẽ sử dụng ứng dụng ví dụ trong bài viết Hiện thực WebSocket với Spring framework, deploy ứng dụng này lên nhiều container khác nhau và sử dụng Nginx để làm load balancer để xem trước khi sử dụng RabbitMQ replay Websocket connection, problem gì sẽ xảy ra!

Trước tiên mình sẽ build Docker Image cho Nginx, nội dung Dockerfile của Nginx như sau:

với tập tin nginx.conf có nội dung ban đầu như sau:

Khác với cấu hình tập tin nginx.conf trong bài viết Deploy ứng dụng load balancer sử dụng Nginx với Docker, trong ví dụ của bài viết này, chúng ta đang làm việc với Websocket connection, chúng ta cần có những cấu hình liên quan đến việc upgrade HTTP connection lên Websocket connection.

Khi Nginx forward Websocket connection request từ client xuống các upstream server, nó cần thêm các header Upgrade và Connection để thiết lập Websocket connection. Các bạn có thể đọc thêm về header Upgrade tại đây.

Giá trị của header Upgrade sẽ được lấy từ biến $http_upgrade, value của biến $http_upgrade này sẽ được Nginx lấy từ header Upgrade trong request từ client.

Còn giá trị của header Connection sẽ được lấy từ biến $connection_upgrade. Định nghĩa của biến $connection_upgrade này sử dụng module map của Nginx như các bạn thấy:

Giá trị mặc định của biến $connection_upgrade luôn là Upgrade, trong trường hợp giá trị của header Upgrade từ request của client là rỗng, giá trị của nó sẽ là close.

Mình sẽ deploy ứng dụng ví dụ lên 3 containers khác nhau, nhưng để cho các bạn thấy rõ problem khi deploy ứng dụng Websocket với load balancer, trước tiên mình chỉ cấu hình để Nginx forward request tới 1 container trước. Trong trường hợp này, chúng ta sẽ không cần relay Websocket conection với một message broker như RabbitMQ.

Để build Docker Image cho Nginx, mình sẽ chạy câu lệnh sau:

Kết quả:

Nội dung của tập tin Dockerfile để build Docker Image cho ứng dụng ví dụ như sau:

Để build Docker Image cho ứng dụng ví dụ này, các bạn nhớ build ứng dụng với command “mvn clean install” trước, sau đó thì hãy chạy câu lệnh sau:

Kết quả:

Docker Compose của mình sẽ có nội dung như sau:

Chạy command “docker compose up” trong thư mục chứa tập tin docker-compose.yaml này, các bạn sẽ thấy kết quả như sau:

Bây giờ nếu các bạn mở 2 cửa sổ trình duyệt và đi đến địa chỉ http://localhost, điền tên của mình vào ô “What is your name?” rồi nhấn nút Send, các bạn sẽ thấy ứng dụng hoạt động bình thường như mình đã làm trong bài viết trước, kết quả của mình như sau:

Nếu giờ các bạn edit tập tin nginx.conf để thêm 2 container khác của ứng dụng ví dụ:

sau đó thì:

  • Stop Docker Compose,
  • Xoá ứng dụng đã được deploy với Docker Compose,
  • Xoá Nginx Docker Image mà chúng ta vừa build ở trên,
  • Build lại Docker Image cho Nginx,
  • Chạy lại Docker Compose,
  • Xong đi đến địa chỉ http://localhost,
  • Điền tên của mình vào ô “What is your name?”
  • Rồi nhấn nút Send,

các bạn sẽ thấy khi mình gửi message ở cửa sổ trình duyệt này, cửa sổ còn lại không nhận message nữa:

Bây giờ, chúng ta sẽ thêm cấu hình cho Websocket của ứng dụng ví dụ để nó relay Websocket connection sử dụng RabbitMQ.

Trước tiên, chúng ta sẽ cần có một RabbitMQ server.

Mình đã hướng dẫn các bạn cách cài đặt RabbitMQ server sử dụng Docker Compose. Docker Image của RabbitMQ trong bài viết hướng dẫn này chưa enable để hỗ trợ STOMP message, giao thức mà ứng dụng ví dụ của chúng ta đang sử dụng để client và server giao tiếp với nhau, nên mình sẽ build một Docker Image mới cho RabbitMQ enable STOMP plugin. Nội dung của Dockerfile của RabbitMQ server sẽ như sau:

Mình sẽ build Docker Image cho RabbitMQ với Dockerfile ở trên với command như sau:

Giờ thì chúng ta có thể khai báo thêm 1 service mới cho RabbitMQ trong tập tin docker-compose.yaml của chúng ta như sau:

Mặc định thì STOMP Plugin sẽ listen ở port 61613 và có default cho system login với username/passcode lần lượt là guest/guest.

Bây giờ thì chúng ta sẽ modify code của class WebSocketConfiguration trong ứng dụng ví dụ để sử dụng StompBrokerRelay với RabbitMQ thay vì SimpleBroker, như sau:

Này là mình đang sử dụng mặc định các thông tin kết nối đến RabbitMQ như host là “localhost”, port là “61613”, system user với passport lần lượt là “guest” và “guest”. Nếu thông tin kết nối khác với các giá trị mặc định, các bạn có thể thêm code như sau:

Mình sẽ deploy ứng dụng với Docker Compose nên giá trị của Host mình set thành 172.17.0.1 như các bạn thấy.

Để làm việc của RabbitMQ, các bạn cũng cần thêm các dependencies sau:

Bây giờ thì các bạn hãy build rồi chạy ứng dụng, sau đó thì mở 2 cửa sổ trình duyệt và làm lại thao tác như trên, các bạn cũng sẽ thấy cả 2 browser đều nhận được message từ WebSocket server.

2 thoughts on “Relay Websocket connection sử dụng RabbitMQ trong Spring Websocket

  1. Em thật sự rất thích các chủ đề về realtime Websocket.Đặc biệt là được viết bằng Spring Boot ngôn ngữ mà em học chuyên sâu.
    Anh cho em hỏi là trên thực tế, các chức năng về realtime thì người ta có dùng Java hay cụ thể hơn là Spring Websocket ko ạ ?
    Ví dụ như các tính năng chat , login bằng QR code như Zalo,…..
    Cảm ơn anh !

Add Comment