前言
websocket ,對于我來說已經是老朋友了,
很久很久以前,我寫過兩篇websocket 相關的文章,
一篇極簡風,最最最基礎的方式整合websocket :
《SpringBoot 整合WebSocket 簡單實戰案例》
地址: https://blog.csdn.net/qq_35387940/article/details/93483678
一篇極限風,最最最完善的方式整合websocket (stomp+rabbitmq處理負載):
《Springboot 整合Websocket,Stomp協議,使用rabbitmq做訊息代理,訊息快取》
地址: https://blog.csdn.net/qq_35387940/article/details/108276136
但是按照最近看官們給我反應情況來看, 一個極簡不符合需求,因為看官們雖然想簡單,但是也想用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").withSockJS();
//點對點
registry.addEndpoint("/privateServer").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 onl oad="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 onl oad="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負載問題 ,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/298085.html
標籤:其他
