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