利用spring?boot+WebSocket實(shí)現(xiàn)后臺(tái)主動(dòng)消息推送功能
前言:
使用此webscoket務(wù)必確保生產(chǎn)環(huán)境能兼容/支持!使用此webscoket務(wù)必確保生產(chǎn)環(huán)境能兼容/支持!使用此webscoket務(wù)必確保生產(chǎn)環(huán)境能兼容/支持!主要是tomcat的兼容與支持。
有個(gè)需求:
APP用戶產(chǎn)生某個(gè)操作,需要讓后臺(tái)管理系統(tǒng)部分人員感知(表現(xiàn)為一個(gè)頁面消息)。
最早版本是后臺(tái)管理系統(tǒng)輪訓(xùn),每隔一段時(shí)間輪訓(xùn)一次,由于消息重要,每隔幾秒就查一次。這樣做明顯很不雅!會(huì)消耗大量資源,并且大部分請(qǐng)求是沒有用的(查不到數(shù)據(jù)進(jìn)來),很藍(lán)瘦。
后來,想著用消息推送的方式來處理這個(gè)邏輯。用戶在app產(chǎn)生了目標(biāo)操作,即產(chǎn)生一個(gè)消息,推送給后臺(tái)管理系統(tǒng)的對(duì)應(yīng)用戶。
然后我就找各種資料,一開始同事推薦dwz,后來發(fā)現(xiàn)不太適用于目前的項(xiàng)目(也許能實(shí)現(xiàn)只是我不知道如何實(shí)現(xiàn))。
后來了解到WebSocket,網(wǎng)上看了很多文檔都是類似聊天室的場(chǎng)景,有些不同。在此,我主要側(cè)重介紹下 服務(wù)器主動(dòng)推送,由服務(wù)端來觸發(fā)。
WebSocket 主要能實(shí)現(xiàn)的場(chǎng)景:
1、網(wǎng)頁聊天室
2、服務(wù)器消息實(shí)時(shí)通知
WebSocket 使用方法應(yīng)該有很多,在次介紹下使用 tomcat8+h5 環(huán)境下的實(shí)現(xiàn)。
ps:我自己的測(cè)試環(huán)境是tomcat7這樣寫是不行的。wang115032337《https://blog.csdn.net/wang115032337》這位朋友在他的環(huán)境下,tomcat7/8都可以用本文章的寫法,只不過需要去除WebSocketConfig類(有文章表示tomcat7和8對(duì)websocket的支持是不同的,本人未深入了解)
話不多說,直接上代碼,想深入了解WebSocket 的請(qǐng)查閱相關(guān)介紹。
1.pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2. 使用@ServerEndpoint創(chuàng)立websocket endpoint( wang115032337這位朋友在他的環(huán)境下加入@ServerEndpoint類會(huì)報(bào)錯(cuò),直接刪除了仍可用)
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3.具體實(shí)現(xiàn)類 可自己選擇url要不要帶參數(shù)
package com.star.manager.service;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
//@ServerEndpoint("/websocket/{user}")
@ServerEndpoint(value = "/websocket")
@Component
public class WebSocketServer {
//靜態(tài)變量,用來記錄當(dāng)前在線連接數(shù)。應(yīng)該把它設(shè)計(jì)成線程安全的。
private static int onlineCount = 0;
//concurrent包的線程安全Set,用來存放每個(gè)客戶端對(duì)應(yīng)的MyWebSocket對(duì)象。
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
//與某個(gè)客戶端的連接會(huì)話,需要通過它來給客戶端發(fā)送數(shù)據(jù)
private Session session;
/**
* 連接建立成功調(diào)用的方法*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在線數(shù)加1
log.info("有新連接加入!當(dāng)前在線人數(shù)為" + getOnlineCount());
try {
sendMessage("連接成功");
} catch (IOException e) {
log.error("websocket IO異常");
}
}
// //連接打開時(shí)執(zhí)行
// @OnOpen
// public void onOpen(@PathParam("user") String user, Session session) {
// currentUser = user;
// System.out.println("Connected ... " + session.getId());
// }
/**
* 連接關(guān)閉調(diào)用的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this); //從set中刪除
subOnlineCount(); //在線數(shù)減1
log.info("有一連接關(guān)閉!當(dāng)前在線人數(shù)為" + getOnlineCount());
}
/**
* 收到客戶端消息后調(diào)用的方法
*
* @param message 客戶端發(fā)送過來的消息*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("來自客戶端的消息:" + message);
//群發(fā)消息
for (WebSocketServer item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("發(fā)生錯(cuò)誤");
error.printStackTrace();
}
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 群發(fā)自定義消息
* */
public static void sendInfo(String message) throws IOException {
log.info(message);
for (WebSocketServer item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
continue;
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
產(chǎn)生一個(gè)消息:產(chǎn)生消息場(chǎng)景有多種,http(s),定時(shí)任務(wù),mq等,這貼一個(gè)httpq請(qǐng)求的controller代碼
@RequestMapping(value="/pushVideoListToWeb",method=RequestMethod.POST,consumes = "application/json")
public @ResponseBody Map<String,Object> pushVideoListToWeb(@RequestBody Map<String,Object> param) {
Map<String,Object> result =new HashMap<String,Object>();
try {
WebSocketServer.sendInfo("有新客戶呼入,sltAccountId:"+CommonUtils.getValue(param, "sltAccountId"));
result.put("operationResult", true);
}catch (IOException e) {
result.put("operationResult", true);
}
return result;
}重要的地方我都加粗了,主要是這段,使用這個(gè)方法,可以實(shí)現(xiàn)服務(wù)器主動(dòng)推送。
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
4.js(html就不寫了,隨便找個(gè)能觸發(fā)這個(gè)js的就可以)
//socket = new WebSocket("ws://localhost:9094/starManager/websocket/張三");
var socket;
if(typeof(WebSocket) == "undefined") {
console.log("您的瀏覽器不支持WebSocket");
}else{
console.log("您的瀏覽器支持WebSocket");
//實(shí)現(xiàn)化WebSocket對(duì)象,指定要連接的服務(wù)器地址與端口 建立連接
//socket = new WebSocket("ws://localhost:9094/starManager/websocket/張三")
socket = new WebSocket("ws://localhost:9094/starManager/websocket");
//打開事件
socket.onopen = function() {
console.log("Socket 已打開");
//socket.send("這是來自客戶端的消息" + location.href + new Date());
};
//獲得消息事件
socket.onmessage = function(msg) {
console.log(msg.data);
//發(fā)現(xiàn)消息進(jìn)入 調(diào)后臺(tái)獲取
getCallingList();
};
//關(guān)閉事件
socket.onclose = function() {
console.log("Socket已關(guān)閉");
};
//發(fā)生了錯(cuò)誤事件
socket.onerror = function() {
alert("Socket發(fā)生了錯(cuò)誤");
}
$(window).unload(function(){
socket.close();
});
// $("#btnSend").click(function() {
// socket.send("這是來自客戶端的消息" + location.href + new Date());
// });
//
// $("#btnClose").click(function() {
// socket.close();
// });
}
簡(jiǎn)單說說:
通過前端代碼
socket = new WebSocket("ws://localhost:9094/starManager/websocket");
其中,starManager是工程名,/webscoket是訪問路徑名
建立連接,前端調(diào)用scoket.open() 會(huì)使后臺(tái)在靜態(tài)成員變量webSocketSet里面增加一個(gè)元素,相當(dāng)于一個(gè)緩存。后臺(tái)服務(wù)調(diào)用sendMessage
(指定某個(gè)用戶,定向)或sendInfo(遍歷webSocketSet逐個(gè)發(fā)送,類似群發(fā))方法,即可向已登錄的客戶端推送消息。
代碼就這么多。我的用這些代碼就跑的起來。做的時(shí)候出現(xiàn)過頁面報(bào)404等錯(cuò)誤,如果也是spring boot+h5,仔細(xì)核對(duì)下和我代碼有無區(qū)別,加配置 路徑是有ok,問題應(yīng)該不大。
如果你恰好也有可以用WebSocket實(shí)現(xiàn)的類似場(chǎng)景,希望對(duì)你有幫助。
總結(jié)
到此這篇關(guān)于利用spring boot+WebSocket實(shí)現(xiàn)后臺(tái)主動(dòng)消息推送功能的文章就介紹到這了,更多相關(guān)springboot+WebSocket消息推送內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
老生常談spring boot 1.5.4 日志管理(必看篇)
下面小編就為大家?guī)硪黄仙U剆pring boot 1.5.4 日志管理(必看篇)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
Java實(shí)現(xiàn)Web應(yīng)用中的定時(shí)任務(wù)(實(shí)例講解)
下面小編就為大家分享一篇Java實(shí)現(xiàn)Web 應(yīng)用中的定時(shí)任務(wù)的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-11-11
java后臺(tái)實(shí)現(xiàn)js關(guān)閉本頁面,父頁面指定跳轉(zhuǎn)或刷新操作
這篇文章主要介紹了java后臺(tái)實(shí)現(xiàn)js關(guān)閉本頁面,父頁面指定跳轉(zhuǎn)或刷新操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-11-11
Spring事務(wù)管理中關(guān)于數(shù)據(jù)庫(kù)連接池詳解
事務(wù)的作用就是為了保證用戶的每一個(gè)操作都是可靠的,事務(wù)中的每一步操作都必須成功執(zhí)行,只要有發(fā)生異常就 回退到事務(wù)開始未進(jìn)行操作的狀態(tài)。事務(wù)管理是Spring框架中最為常用的功能之一,我們?cè)谑褂肧pring Boot開發(fā)應(yīng)用時(shí),大部分情況下也都需要使用事務(wù)2022-12-12
Java類成員訪問權(quán)限控制知識(shí)總結(jié)
這篇文章主要介紹了Java類成員訪問權(quán)限控制知識(shí)總結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
SpringBoot?2.5.5整合輕量級(jí)的分布式日志標(biāo)記追蹤神器TLog的詳細(xì)過程
分布式追蹤系統(tǒng)是一個(gè)最終的解決方案,如果您的公司已經(jīng)上了分布式追蹤系統(tǒng),這篇文章主要介紹了SpringBoot?2.5.5整合輕量級(jí)的分布式日志標(biāo)記追蹤神器TLog,需要的朋友可以參考下2022-10-10
java 使用簡(jiǎn)單的demo實(shí)例告訴你優(yōu)化算法的強(qiáng)大
本篇文章介紹了,在java中使用簡(jiǎn)單的demo實(shí)例告訴你優(yōu)化算法的強(qiáng)大。需要的朋友參考下2013-05-05
idea啟動(dòng)springmvc項(xiàng)目時(shí)報(bào)找不到類的解決方法
這篇文章主要介紹了idea啟動(dòng)springmvc項(xiàng)目時(shí)報(bào)找不到類的解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
Java單元測(cè)試Powermockito和Mockito使用總結(jié)
公司單元測(cè)試框架選用了Junit 4.12,Mock框架選用了Mockito和PowerMock,本文主要介紹了Java單元測(cè)試Powermockito和Mockito使用總結(jié),感興趣的可以了解一下2021-09-09

