Hiện thực WebSocket với Spring framework – Hướng Dẫn Java

WebSocket là một trong những loại Socket, nói nôm na cho những bạn dễ tưởng tượng về WebSocket thì nó giúp tất cả chúng ta hoàn toàn có thể tạo liên kết 2 chiều giữa server side và client side trong một web application bất kể. Một khi liên kết đã được thiết lập thì client và server hoàn toàn có thể trao đổi những thông tin với nhau, client hoàn toàn có thể gửi message cho server và ngược lại server cũng vậy. Khác với HTTP connection thì chỉ có client mới hoàn toàn có thể gửi request tới server, server trả về response, server không hề tự gửi message tới bất kể client nào. WebSocket thường được sử dụng trong những ứng dụng web yên cầu tính realtime của một tính năng nào đó. Trong bài viết này, mình sẽ hướng dẫn những bạn cách hiện thực WebSocket trong một web application với Spring framework những bạn nhé !
Đầu tiên, mình sẽ tạo mới một Spring Boot project :

Hiện thực WebSocket với Spring framework

với Web và WebSocket dependency như sau :

Hiện thực WebSocket với Spring framework

để làm ví dụ .

Kết quả :

Hiện thực WebSocket với Spring framework

Tạo mới WebSocket server

Để khởi tạo WebSocket server với Spring WebSocket, thứ nhất, những bạn cần phải nắm một số ít khái niệm như sau :
Đầu tiên là về Message Broker, nó là một message-oriented middleware server đứng ở giữa để delivery message từ những request tới những topic theo chính sách pub-sub hoặc queue theo chính sách point-to-point. Có nghĩa là thay vì những application gửi thẳng message tới topic hoặc queue mà những bạn thường thấy khi thao tác với Message Queue thì với Message Broker, những message phải đi qua Message Broker này. Spring sử dụng Message Broker để hiện thực WebSocket behind the sense đó những bạn !

Cái thứ hai là mình sẽ nói về STOMP. STOMP là gì ? Nó là viết tắt của từ Streaming Text Oriented Messaging Protocol, dịch ra thì STOMP là một giao thức tin nhắn hướng văn bản, được sử dụng để client và server sau khi đã connect được với nhau, sử dụng để trao đổi thông tin. Spring cũng tương hỗ giao thức này trong việc truyền thông tin giữa client và server với WebSocket đó những bạn .
OK, giờ tất cả chúng ta sẽ khai báo để tạo mới một WebSocket server những bạn nhé !
Các bạn cần tạo mới một class để thông số kỹ thuật cho WebSocket. Class này sẽ implement interface WebSocketMessageBrokerConfigurer và được annotate với annotation @ EnableWebSocketMessageBroker nha những bạn :

1234567891011

packagecom.huongdanjava.springboot.websocket;

importorg.springframework.context.annotation.Configuration;

importorg.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;

importorg.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@ Configuration

@ EnableWebSocketMessageBroker

publicclassWebSocketConfigurationimplementsWebSocketMessageBrokerConfigurer{

}

Tất nhiên, là nó cũng cần được annotate với annotation @ Configuration để Spring Boot tự động hóa scan nữa !

Có 2 phương pháp mà tất cả chúng ta cần implement trong class WebSocketConfiguration này là configureMessageBroker ( ) và registerStompEndpoints ( ). Phương thức configureMessageBroker ( ) với tham số là class MessageBrokerRegistry được cho phép tất cả chúng ta hoàn toàn có thể thông số kỹ thuật Message Broker với phương pháp enableSimpleBroker ( ) :

12345

@ Override

publicvoidconfigureMessageBroker(MessageBrokerRegistryregistry){

registry.enableSimpleBroker(” / topic “);

registry.setApplicationDestinationPrefixes(” / app “);

}

Tham số của phương pháp enableSimpleBroker ( ) là prefix của những endpoint mà những client hoàn toàn có thể subscribe và nhận message từ server. Có nghĩa là, endpoint mà những client subscribe phải khởi đầu với giá trị được khai báo với phương pháp enableSimpleBroker ( ). Khác thì ứng dụng của tất cả chúng ta sẽ không chạy .
Trong phương pháp configureMessageBroker ( ) trên, mình cũng sử dụng thêm phương pháp setApplicationDestinationPrefixes ( ) để định nghĩa prefix cho những destination mà client sẽ gửi message tới WebSocket server. Các bạn nếu đã thao tác với RESTful Web Service sử dụng Spring MVC thì hoàn toàn có thể tưởng tượng mục tiêu của phương pháp này giống như những bạn định nghĩa một request mapping trong Controller ở class level. Những method định nghĩa những request URL sẽ có prefix là value của request mapping này .
Phương thức registerStompEndpoints ( ) với tham số là class StompEndpointRegistry thì cũng giống như khi định nghĩa những request URL trong RESTful Web Service, giúp tất cả chúng ta định nghĩa những endpoint mà client sẽ sử dụng để gọi và liên kết tới WebSocket. Chúng ta sử dụng phương pháp addEndpoint ( ) của class StompEndpointRegistry để thêm những endpoint mà những bạn muốn. Ví dụ mình định nghĩa một endpoint như sau :

1234

@ Override

publicvoidregisterStompEndpoints(StompEndpointRegistryregistry){

registry.addEndpoint(” / hello “);

}

Một số trình duyệt hoàn toàn có thể sẽ không tương hỗ WebSocket connection ví dụ như chính sách ẩn danh của trình duyệt Chrome ví dụ điển hình. Trong trường hợp này, những bạn hoàn toàn có thể gọi thêm method withSockJS ( ) để sử dụng những giải pháp thay thế sửa chữa khác như xhr-streaming, xhr-polling thay vì liên kết WebSocket mặc định .
Nội dung class WebSocketConfiguration của mình lúc này như sau :

1234567891011121314151617181920212223

packagecom.huongdanjava.springboot.websocket;

importorg.springframework.context.annotation.Configuration;

importorg.springframework.messaging.simp.config.MessageBrokerRegistry;

importorg.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;

importorg.springframework.web.socket.config.annotation.StompEndpointRegistry;

importorg.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@ Configuration

@ EnableWebSocketMessageBroker

publicclassWebSocketConfigurationimplementsWebSocketMessageBrokerConfigurer{

@ Override

publicvoidregisterStompEndpoints(StompEndpointRegistryregistry){

registry.addEndpoint(” / hello “).withSockJS();

}

@ Override

publicvoidconfigureMessageBroker(MessageBrokerRegistryregistry){

registry.enableSimpleBroker(” / topic “);

registry.setApplicationDestinationPrefixes(” / app “);

}

}

Để handle message từ client gửi tới endpoint mà tất cả chúng ta vừa mới configure ở trên “ / hello ”, những bạn hoàn toàn có thể tạo mới một controller MessageController với nội dung như sau :

123456789101112131415

packagecom.huongdanjava.springboot.websocket;

importorg.springframework.messaging.handler.annotation.MessageMapping;

importorg.springframework.messaging.handler.annotation.SendTo;

importorg.springframework.stereotype.Controller;

@ Controller

publicclassMessageController{

@ MessageMapping(” / hello “)

@ SendTo(” / topic / messages “)

publicStringsend(Stringusername){

return” Hello, “+username;

}

}

Giống như controller trong Spring MVC, những bạn hoàn toàn có thể annotate class handle message từ client với annotation @ Controller nhưng thay vì sử dụng annotation @ RequestMapping trong method handle message, tất cả chúng ta sử dụng annotation @ MessageMapping với value là giá trị của endpoint mà tất cả chúng ta đã thông số kỹ thuật trong class WebSocketConfiguration, ví dụ của mình là “ / hello ” .

Sau khi method giải quyết và xử lý business logic và trả về tác dụng, tác dụng này sẽ được gửi tới destination mà client đã subscribe, được khai báo trong annotation @ SendTo. Nên nhớ là giá trị được khai báo trong annotation @ SendTo phải mở màn giống với giá trị tất cả chúng ta đã khai báo trong phương pháp enableSimpleBroker ( ) ở trên nha những bạn !
Bây giờ, nếu những bạn chạy ứng dụng, xem log message, những bạn sẽ thấy WebSocket server sẽ start và chuẩn bị sẵn sàng nhận liên kết từ phía client như sau :

1234567891011121314151617181920212223

._________

/\\/___’ _ __ _ _ ( _ ) _ __ __ _ \ \ \ \

( ( ) \ ___ | ‘_|’ _ | | ‘_\/_`|\\\\

\\/___)||_)|||||||(_||))))

‘ | ____ |. __ | _ | | _ | _ | | _ \ __, | / / / /

= = = = = = = = = | _ | = = = = = = = = = = = = = = | ___ / = / _ / _ / _ /

:: Spring Boot :: ( v2. 5.0 )

2021 – 06-05 11:36:45. 108 INFO 38061 — [ main ] c. h. s. w. SpringBootWebsocketApplication : Starting SpringBootWebsocketApplication using Java 15.0.1 on Khanhs-MBP with PID 38061 ( / Users / khanh / Documents / workspace-spring-tool-suite-4-4. 9.0. RELEASE / spring-boot-websocket / target / classes started by khanh in / Users / khanh / Documents / workspace-spring-tool-suite-4-4. 9.0. RELEASE / spring-boot-websocket )

2021 – 06-05 11:36:45. 110 INFO 38061 — [ main ] c. h. s. w. SpringBootWebsocketApplication : No active profile set, falling back to default profiles : default

2021 – 06-05 11:36:46. 683 INFO 38061 — [ main ] o.s.b.w.embedded.tomcat. TomcatWebServer : Tomcat initialized with port ( s ) : 8080 ( http )

2021 – 06-05 11:36:46. 694 INFO 38061 — [ main ] o.apache.catalina.core. StandardService : Starting service [ Tomcat ]

2021 – 06-05 11:36:46. 694 INFO 38061 — [ main ] org.apache.catalina.core. StandardEngine : Starting Servlet engine : [ Apache Tomcat / 9.0.46 ]

2021 – 06-05 11:36:46. 860 INFO 38061 — [ main ] o. a. c. c. C. [ Tomcat ]. [ localhost ]. [ / ] : Initializing Spring embedded WebApplicationContext

2021 – 06-05 11:36:46. 860 INFO 38061 — [ main ] w. s. c. ServletWebServerApplicationContext : Root WebApplicationContext : initialization completed in 1609 ms

2021 – 06-05 11:36:47. 306 INFO 38061 — [ main ] o.s.b.w.embedded.tomcat. TomcatWebServer : Tomcat started on port ( s ) : 8080 ( http ) with context path ”

2021-06-0511:36:47.307INFO38061—[main]o.s.m.s.b.SimpleBrokerMessageHandler:Starting…

2021-06-0511:36:47.307INFO38061—[main]o.s.m.s.b.SimpleBrokerMessageHandler:BrokerAvailabilityEvent[available=true,SimpleBrokerMessageHandler[org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry@ 6 a1ef65c]]

2021-06-0511:36:47.308INFO38061—[main]o.s.m.s.b.SimpleBrokerMessageHandler:Started.

2021-06-0511:36:47.319INFO38061

—[main]c.h.s.w.SpringBootWebsocketApplication:StartedSpringBootWebsocketApplicationin2.769seconds(JVMrunningfor3.982)

2021-06-0511:36:47.320INFO38061—[main]o.s.b.a.ApplicationAvailabilityBean:ApplicationavailabilitystateLivenessStatechangedtoCORRECT

2021-06-0511:36:47.321INFO38061—[main]o.s.b.a.ApplicationAvailabilityBean:ApplicationavailabilitystateReadinessStatechangedtoACCEPTING_TRAFFIC

2021-06-0511:37:47.023INFO38061—[MessageBroker-1]o.s.w.s.c.WebSocketMessageBrokerStats:WebSocketSession[0currentWS(0)-HttpStream(0)-HttpPoll(0),0total,0closedabnormally(0connectfailure,0sendlimit,0transporterror)],stompSubProtocol[processedCONNECT(0)-CONNECTED(0)-DISCONNECT(0)],stompBrokerRelay[null],inboundChannel[poolsize=0,activethreads=0,queuedtasks=0,completedtasks=0],outboundChannel[poolsize=0,activethreads=0,queuedtasks=0,completedtasks=0],sockJsScheduler[poolsize=1,activethreads=1,queuedtasks=0,completedtasks=0]

Tiếp theo, tất cả chúng ta sẽ code phần client để xem WebSocket hoạt động giải trí như thế nào nhé những bạn !

Tạo mới client kết nối tới WebSocket server

Mình sẽ viết code HTML, JavaScript để giả lập việc gửi message từ client tới server, và cả việc server gửi message tới client như thế nào .

Mình sẽ sử dụng WebJars để thêm JQuery, SocketJS client và Stomp WebSocket dependencies như sau :

12345678910111213141516171819

org.webjars

webjars-locator-core

org.webjars.npm

jquery

3.6.0

org.webjars

sockjs-client

1.5.1

org.webjars

stomp-websocket

2.3.4

JQuery dùng để viết code Javascript thuận tiện hơn, SockJS để thao tác với WebSocket connection còn Stomp WebSocket thì để tất cả chúng ta thao tác với STOMP message đó những bạn !
Mình sẽ tạo mới một trang HTML được cho phép tất cả chúng ta nhập tên user, nhấn nút gửi tới WebSocket server, và một nơi để hiển thị hiệu quả trả về từ WebSocket server .

Hiện thực WebSocket với Spring framework

Nội dung của tập tin index.html trong thư mục src / main / resources / static như sau :

1234567891011121314151617181920212223242526

html

Hello WebSocket

id=” main-content “>

What is your name ?

type=” text “id=” name “placeholder=” Your name here … “>

Message from server :

id=” message “>

Tập tin app.js cũng trong thư mục src / main / resources / static có nội dung như sau :

12345678910111213141516171819202122232425

varstompClient=null;

USD(document).ready(function(){

connect();

});

functionconnect(){

varsocket=newSockJS(‘ / hello ‘);

stompClient=Stomp.over(socket);

stompClient.connect({},function(){

console.log(‘ Web Socket is connected ‘);

stompClient.subscribe(‘ / topic / messages ‘,function(message){

USD(” # message “).text(message.body toàn thân);

});

});

}

USD(function(){

USD(” form “).on(‘ submit ‘,function(e){

e.preventDefault();

});

USD(” # send “).click(function(){

stompClient.send(” / app / hello “,{},USD(” # name “).val());

});

});

Nếu những bạn biết một chút ít về JQuery thì sẽ hiểu code của mình có nghĩa là : khi browser đã load nội dung của trang index.html, web của mình sẽ tự động hóa connect tới WebSocket server sử dụng thư viện SockJS client và sử dụng thư viện Stomp WebSocket để gửi STOMP message. Đối tượng Stomp Client sẽ subscribe vào “ / topic / messages ” để nhận message từ WebSocket server .
Stomp Client được cho phép tất cả chúng ta hoàn toàn có thể gửi một STOMP message tới WebSocket server sử dụng endpoint “ / app / hello ” với giá trị tất cả chúng ta nhập trong textbox .
Mỗi khi nhận message từ WebSocket server thì giá trị trong phần body toàn thân của STOMP message này sẽ được hiển thị .

Kết quả khi mình chạy ứng dụng, nhập Khanh vào textbox và nhấn nút Send như sau :

Hiện thực WebSocket với Spring framework

Nếu những bạn mở 2 hành lang cửa số trình duyệt và làm lại thao tác như trên, những bạn cũng sẽ thấy cả 2 browser đều nhận được message từ WebSocket server. Của mình như sau :

Hiện thực WebSocket với Spring framework