irpas技术客

Springboot 整合 WebSocket ,使用STOMP协议 ,前后端整合实战 (一)_小目标青年_springboot stomp

大大的周 1462

前言

websocket ,对于我来说已经是老朋友了。?

?很久很久以前,我写过两篇websocket 相关的文章。

??一篇极简风,最最最基础的方式整合websocket :

《SpringBoot 整合WebSocket 简单实战案例》

地址:?SpringBoot 整合WebSocket 简单实战案例_默默不代表沉默-CSDN博客_springboot websocket

一篇极限风,最最最完善的方式整合websocket (stomp+rabbitmq处理负载):

?《Springboot 整合Websocket,Stomp协议,使用rabbitmq做消息代理,消息缓存》

?地址:?Springboot 整合Websocket+Stomp协议+RabbitMQ做消息代理 实例教程_默默不代表沉默-CSDN博客_rabbitmq stomp协议

但是按照最近看官们给我反应情况来看, 一个极简不符合需求,因为看官们虽然想简单,但是也想用stomp。

然而那个极限的呢,又太复杂,看官们不乐意去整合rabbitmq。

那么,这篇文章的意义就出来了。 ?

正文

本篇内容:

1.后端整合websocket (STOMP协议)

2.群发、指定单发

3.前端简单页面示例(接收、发送消息)

事不宜迟,开始敲代码。

先看下这次实战案例项目结构:

1. pom.xml 核心依赖的导入: ?

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>

2.application.yml? (我就单纯设置一下端口) ?

server: port: 9908

3.WebSocketConfig.java

import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; /** * @Author JCccc * @Description EnableWebSocketMessageBroker-注解开启STOMP协议来传输基于代理的消息,此时控制器支持使用@MessageMapping * @Date 2021/6/30 8:53 */ @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { //topic用来广播,user用来实现点对点 config.enableSimpleBroker("/topic", "/user"); } /** * 开放节点 * @param registry */ @Override public void registerStompEndpoints(StompEndpointRegistry registry) { //注册两个STOMP的endpoint,分别用于广播和点对点 //广播 registry.addEndpoint("/publicServer").setAllowedOrigins("*").withSockJS(); //点对点 registry.addEndpoint("/privateServer").setAllowedOrigins("*").withSockJS(); } }

4.推送消息的实体类?Message.java

/** * @Author JCccc * @Description * @Date 2021/8/20 9:26 */ public class Message { /** * 消息编码 */ private String code; /** * 来自(保证唯一) */ private String form; /** * 去自(保证唯一) */ private String to; /** * 内容 */ private String content; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getForm() { return form; } public void setForm(String form) { this.form = form; } public String getTo() { return to; } public void setTo(String to) { this.to = to; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }

5.前端简单调试页面?

① publicExample.html 监听广播消息的测试页面

<html> <head> <meta charset="UTF-8"> <title>等系统推消息</title> <script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script> <script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script> <script src="https://code.jquery.com/jquery-3.2.0.min.js" integrity="sha256-JAW99MJVpJBGcbzEuXk4Az05s/XyDdBomFqNlM3ic+I=" crossorigin="anonymous"></script> <script type="text/javascript"> var stompClient = null; function setConnected(connected) { document.getElementById("connect").disabled = connected; document.getElementById("disconnect").disabled = !connected; $("#response").html(); } function connect() { var socket = new SockJS("http://localhost:9908/publicServer"); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { setConnected(true); console.log('Connected: ' + frame); stompClient.subscribe('/topic/all', function (response) { var responseData = document.getElementById('responseData'); var p = document.createElement('p'); p.style.wordWrap = 'break-word'; p.appendChild(document.createTextNode(response.body)); responseData.appendChild(p); }); },{}); } function disconnect() { if (stompClient != null) { stompClient.disconnect(); } setConnected(false); console.log("Disconnected"); } function sendMsg() { var content = document.getElementById('content').value; stompClient.send("/all",{},JSON.stringify({'content': content})); } </script> </head> <body onload="disconnect()"> <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being enabled. Please enable Javascript and reload this page!</h2></noscript> <div> <div> <labal>连接广播频道</labal> <button id="connect" onclick="connect();">Connect</button> <labal>取消连接</labal> <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button> </div> <div id="conversationDiv"> <labal>广播消息</labal> <input type="text" id="content"/> <button id="sendMsg" onclick="sendMsg();">Send</button> </div> <div> <labal>接收到的消息:</labal> <p id="responseData"></p> </div> </div> </body> </html>

简析:?

趁热打铁,我们模拟系统后端给前端推送广播消息,通过接口模拟: ?

TestController.java

/** * @Author JCccc * @Description * @Date 2021/8/20 8:53 */ @Controller public class TestController { @Autowired public SimpMessagingTemplate template; /** * 广播 * * @param msg */ @ResponseBody @RequestMapping("/pushToAll") public void subscribe( @RequestBody Message msg) { template.convertAndSend("/topic/all", msg.getContent()); } }

简析:

我们推送消息,直接用?SimpMessagingTemplate ,

用的是convertAndSend 广播方式推送到对于的主题目的地 destination 。

(可以看到其实还有convertAndSendToUser ,不着急,后面会说,这是发送给某个连接用户的)

?直接把项目跑起来,打开页面开始测试:

?我们先点击connect ,连接成功:

可以看到实际上stomp.min.js 最终也是转化成为 ws/wss这种方式成功连接:

调用测试接口,推送广播消息:

?

?在console其实也能看到:

?广播功能就到这,接下来是 点对点。

前端页面: privateExample.html

<html> <head> <meta charset="UTF-8"> <title>聊起来</title> <script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script> <script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script> <script src="https://code.jquery.com/jquery-3.2.0.min.js" integrity="sha256-JAW99MJVpJBGcbzEuXk4Az05s/XyDdBomFqNlM3ic+I=" crossorigin="anonymous"></script> <script type="text/javascript"> var stompClient = null; function setConnected(connected) { document.getElementById("connect").disabled = connected; document.getElementById("disconnect").disabled = !connected; $("#response").html(); } function connect() { var socket = new SockJS("http://localhost:9908/privateServer"); stompClient = Stomp.over(socket); stompClient.heartbeat.outgoing = 20000; // client will send heartbeats every 20000ms stompClient.heartbeat.incoming = 0; stompClient.connect({}, function (frame) { setConnected(true); console.log('Connected: ' + frame); stompClient.subscribe('/user/' + document.getElementById('user').value + '/message', function (response) { var responseData = document.getElementById('responseData'); var p = document.createElement('p'); p.style.wordWrap = 'break-word'; p.appendChild(document.createTextNode(response.body)); responseData.appendChild(p); }); }); } function disconnect() { if (stompClient != null) { stompClient.disconnect(); } setConnected(false); console.log("Disconnected"); } function sendMsg() { var headers = { login: 'mylogin', passcode: 'mypasscode', // additional header 'accessToken': 'HWPO325J9814GBHJF933' }; var content = document.getElementById('content').value; var to = document.getElementById('to').value; stompClient.send("/alone", {'accessToken': 'HWPO325J9814GBHJF933'}, JSON.stringify({ 'content': content, 'to': to })); } </script> </head> <body onload="disconnect()"> <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being enabled. Please enable Javascript and reload this page!</h2></noscript> <div> <div> <labal>连接用户</labal> <input type="text" id="user"/> <button id="connect" onclick="connect();">Connect</button> </div> <div> <labal>取消连接</labal> <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button> </div> <div id="conversationDiv"> <labal>发送消息</labal> <div> <labal>内容</labal> <input type="text" id="content"/> </div> <div> <labal>发给谁</labal> <input type="text" id="to"/> </div> <button id="sendMsg" onclick="sendMsg();">Send</button> </div> <div> <labal>接收到的消息:</labal> <p id="responseData"></p> </div> </div> </body> </html>

简析:

趁热打铁,我们模拟系统后端给前端推送点对点消息(指定某个用户),通过接口模拟:

/** * 点对点 */ @ResponseBody @RequestMapping("/pushToOne") public void queue(@RequestBody Message msg) { System.out.println("进入方法"); /*使用convertAndSendToUser方法,第一个参数为用户id,此时js中的订阅地址为 "/user/" + 用户Id + "/message",其中"/user"是固定的*/ template.convertAndSendToUser(msg.getTo(), "/message", msg.getContent()); }

用的是convertAndSendToUser 推送到指定的用户 ,对于的主题目的地 destination(/message)?

也许看到这里,你会觉得很奇怪,为什么我们推的主题是 /message,但是前端订阅的却是 ?

"/user/" + 用户Id + "/message"

我们直接看源码:

?

?ok,应该不用多说,代码帮我们自己拼接起来了,跟前端订阅规则保持一致。

直接跑项目,测试一下:

模拟我们连接的用户标识 19901 ,连接成功

使用postman调用我们的测试接口,模拟系统指定推送消息到 19901 这个人 :

然后也给20011发送一下消息:

?然后看到对应的用户也能收到对应的消息:

对点推送,广播推送,也已经完毕了?。

这种情况就是相当于使用http接口方式,去撮合后端服务做 消息推送。

其实spring还提供了一些好玩的注解,

@MessageMapping? 这个注解是对称于??@EnableWebSocketMessageBroker

也就是说,如果我们使用@EnableWebSocketMessageBroker ,那么我们在接口上面其实就能直接使用??@MessageMapping。

怎么用?

然后前端代码里面?的使用:

?

?这样我们就可以通过前端直接调用相关接口了:

个人认为其实没有必要使用这个注解,直接通过前端调用后端服务代码,我们服务端来根据Message里面 的 发送方、接收方、消息类型(点对点、广播)就可以直接完成相关也业务场景了。

ok,该篇就到此。

下一篇,给大家带来怎么解决websocket负载问题 。


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #springboot #stomp #使用 #websocket的文章 #整合WebSocket