Springboot+WebSocket實現消息推送

WebSocket是一種在單個TCP連接上進行全雙工通信的協議。WebSocket通信協議于2011年被IETF定為標準RFC 6455,并由RFC7936補充規范。WebSocketAPI也被W3C定為標準。

WebSocket使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在WebSocket API中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,并進行雙向數據傳輸。
創建定時任務,實現定時向前端推送相關消息。
創建存放ws推送的參數緩存Map,定時任務獲取參數,獲取數據后推送。

引入依賴

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

開啟WebSocket支持的配置類

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** 功能描述:* 開啟websocket支持*/
@Configuration
public class WebSocketConfig {// 使用boot內置tomcat時需要注入此bean@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

WebSocketServer服務端

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;/*** 功能描述:* WebSocketServer服務端*/
// @ServerEndpoint 注解是一個類層次的注解,它的功能主要是將目前的類定義成一個websocket服務器端。注解的值將被用于監聽用戶連接的終端訪問URL地址
// encoders = WebSocketCustomEncoding.class 是為了使用ws自己的推送Object消息對象(sendObject())時進行解碼,通過Encoder 自定義規則(轉換為JSON字符串)
@ServerEndpoint(value = "/websocket/{userId}",encoders = WebSocketCustomEncoding.class)
@Component
public class WebSocket {private final static Logger logger = LogManager.getLogger(WebSocket.class);/*** 靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的*/private static int onlineCount = 0;/*** concurrent包的線程安全Map,用來存放每個客戶端對應的MyWebSocket對象*/public static ConcurrentHashMap<String, WebSocket> webSocketMap = new ConcurrentHashMap<>();/**** 功能描述:* concurrent包的線程安全Map,用來存放每個客戶端對應的MyWebSocket對象的參數體*/public static ConcurrentHashMap<String, PushParams> webSocketParamsMap = new ConcurrentHashMap<>();/*** 與某個客戶端的連接會話,需要通過它來給客戶端發送數據*/private Session session;private String userId;/*** 連接建立成功調用的方法* onOpen 和 onClose 方法分別被@OnOpen和@OnClose 所注解。他們定義了當一個新用戶連接和斷開的時候所調用的方法。*/@OnOpenpublic void onOpen(Session session, @PathParam("userId") String userId) {this.session = session;this.userId = userId;//加入mapwebSocketMap.put(userId, this);addOnlineCount();           //在線數加1logger.info("用戶{}連接成功,當前在線人數為{}", userId, getOnlineCount());try {sendMessage(String.valueOf(this.session.getQueryString()));} catch (IOException e) {logger.error("IO異常");}}/*** 連接關閉調用的方法*/@OnClosepublic void onClose() {//從map中刪除webSocketMap.remove(userId);subOnlineCount();           //在線數減1logger.info("用戶{}關閉連接!當前在線人數為{}", userId, getOnlineCount());}/*** 收到客戶端消息后調用的方法* onMessage 方法被@OnMessage所注解。這個注解定義了當服務器接收到客戶端發送的消息時所調用的方法。* @param message 客戶端發送過來的消息*/@OnMessagepublic void onMessage(String message, Session session) {logger.info("來自客戶端用戶:{} 消息:{}",userId, message);//群發消息/*for (String item : webSocketMap.keySet()) {try {webSocketMap.get(item).sendMessage(message);} catch (IOException e) {e.printStackTrace();}}*/}/*** 發生錯誤時調用** @OnError*/@OnErrorpublic void onError(Session session, Throwable error) {logger.error("用戶錯誤:" + this.userId + ",原因:" + error.getMessage());error.printStackTrace();}/*** 向客戶端發送消息*/public void sendMessage(String message) throws IOException {this.session.getBasicRemote().sendText(message);//this.session.getAsyncRemote().sendText(message);}/*** 向客戶端發送消息*/public void sendMessage(Object message) throws IOException, EncodeException {this.session.getBasicRemote().sendObject(message);//this.session.getAsyncRemote().sendText(message);}/*** 通過userId向客戶端發送消息*/public void sendMessageByUserId(String userId, String message) throws IOException {logger.info("服務端發送消息到{},消息:{}",userId,message);if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){webSocketMap.get(userId).sendMessage(message);}else{logger.error("用戶{}不在線",userId);}}/*** 通過userId向客戶端發送消息*/public void sendMessageByUserId(String userId, Object message) throws IOException, EncodeException {logger.info("服務端發送消息到{},消息:{}",userId,message);if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){webSocketMap.get(userId).sendMessage(message);}else{logger.error("用戶{}不在線",userId);}}/*** 通過userId更新緩存的參數*/public void changeParamsByUserId(String userId, PushParams pushParams) throws IOException, EncodeException {logger.info("ws用戶{}請求參數更新,參數:{}",userId,pushParams.toString());webSocketParamsMap.put(userId,pushParams);}/*** 群發自定義消息*/public static void sendInfo(String message) throws IOException {for (String item : webSocketMap.keySet()) {try {webSocketMap.get(item).sendMessage(message);} catch (IOException e) {continue;}}}public static synchronized int getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {WebSocket.onlineCount++;}public static synchronized void subOnlineCount() {WebSocket.onlineCount--;}}

Encoder 自定義規則(轉換為JSON字符串)

import com.alibaba.fastjson.JSON;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;/*** 在 websocket 中直接發送 obj 會有問題 - No encoder specified for object of class* 需要對 obj 創建解碼類,實現 websocket 中的 Encoder.Text<>* */
public class WebSocketCustomEncoding implements Encoder.Text<Object> {/*** The Encoder interface defines how developers can provide a way to convert their* custom objects into web socket messages. The Encoder interface contains* subinterfaces that allow encoding algorithms to encode custom objects to:* text, binary data, character stream and write to an output stream.** Encoder 接口定義了如何提供一種方法將定制對象轉換為 websocket 消息* 可自定義對象編碼為文本、二進制數據、字符流、寫入輸出流*  Text、TextStream、Binary、BinaryStream* */@Overridepublic void init(EndpointConfig endpointConfig) {}@Overridepublic void destroy() {}@Overridepublic String encode(Object o) throws EncodeException {return JSON.toJSONString(o);}
}

自定義消息推送的參數體

/*** 功能描述:** @description: ws推送的參數結構*/
@Data
public class PushParams {/*** 功能描述:* 類型*/private String type;/*** 功能描述:* 開始時間*/private String startTime;/*** 功能描述:* 結束時間*/private String stopTime;
}

根據用戶ID更新ws推送的參數,或者使用onMessage修改緩存的結構體

import com.company.project.common.websocket.PushParams;
import com.company.project.common.websocket.WebSocket;
import com.company.project.service.TestMongodbService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.websocket.EncodeException;
import java.io.IOException;/*** 功能描述:* 建立WebSocket連接* @Author: LXD* @Date: 2022-12-01 09:55:00* @since: 1.0.0*/
@RestController
@RequestMapping("/webSocketPush")
public class WebSocketController {@Autowiredprivate WebSocket webSocket;@Autowiredprivate TestMongodbService testMongodbService;@RequestMapping("/sentMessage")public void sentMessage(String userId,String message){try {webSocket.sendMessageByUserId(userId,message);} catch (IOException e) {e.printStackTrace();}}@RequestMapping("/sentObjectMessage")public void sentObjectMessage(String userId){try {webSocket.sendMessageByUserId(userId,testMongodbService.query());} catch (IOException e) {e.printStackTrace();} catch (EncodeException e) {e.printStackTrace();}}/**** 功能描述:* 根據用戶ID更新ws推送的參數* @Param  userId: WS中的用戶ID* @Param pushParams: 推送參數* @return: void* @since: 1.0.0*/@RequestMapping("/changeWsParams")public void changeWsParams(String userId, PushParams pushParams){try {webSocket.changeParamsByUserId(userId,pushParams);} catch (IOException e) {e.printStackTrace();} catch (EncodeException e) {e.printStackTrace();}}}

創建定時推送的任務

import com.company.project.service.TestMongodbService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import javax.websocket.EncodeException;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import static com.company.project.common.websocket.WebSocket.webSocketMap;
import static com.company.project.common.websocket.WebSocket.webSocketParamsMap;/*** 功能描述:** @description: ws定時推送*/
@Configuration
@EnableScheduling
public class WebsocketSchedule {@Autowiredprivate WebSocket webSocket;@Autowiredprivate TestMongodbService testMongodbService;// 第一次延遲1秒后執行,之后按fixedRate的規則每5秒執行一次 fixedRateString 與 fixedRate 意思相同,只是使用字符串的形式。唯一不同的是支持占位符@Scheduled(initialDelay=1000, fixedRateString = "${ws.pushInterval}")public void pushData() throws EncodeException, IOException {ConcurrentHashMap<String, WebSocket> webSocketPushMap = webSocketMap;ConcurrentHashMap<String, PushParams> webSocketPushParamsMap = webSocketParamsMap;if(!webSocketPushMap.isEmpty()){for(String key : webSocketPushMap.keySet()){// 根據ws連接用戶ID獲取推送參數PushParams pushParams = webSocketPushParamsMap.get(key);webSocket.sendMessageByUserId(key,testMongodbService.query());}}}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/20371.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/20371.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/20371.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

學習率調整

學習率調整 import mathdef adjust_learning_rate(optimizer, epoch, args):"""Decay the learning rate with half-cycle cosine after warmup"""if epoch < args.warmup_epochs:lr args.lr * epoch / args.warmup_epochs else:lr args.m…

不是,你不會還在用雙層遍歷循環來做新舊數組對比,尋找新增元素吧?

目錄 一、雙層循環遍歷 1.1、雙循環錯誤示范 1.2、正確的做法 ①使用array.includes() ②使用set 二、array.includes()的使用與技巧 2.1、基本語法 2.2、返回值 2.3、使用技巧 2.3.1、用戶輸入驗證 2.3.2、權限檢查 2.4、兼容問題 三、總結 一、雙層循環遍歷 1.…

【重學C語言】十七、預處理指令

【重學C語言】十七、預處理指令 預處理指令預定義宏`#define` 宏定義示例注意事項特殊符號條件編譯頭文件包含`#pragma`預處理指令 C語言中的預處理指令(Preprocessor Directives)是一種特殊的指令,它們在編譯過程的早期階段(即實際編譯之前)被預處理器(Preprocessor)處…

OpenCV學習 基礎圖像操作(十六):圖像距離變換

基礎原理 顧名思義&#xff0c;我們可以利用像素之間的距離作為對該像素的一種刻畫&#xff0c;并將其運用到相應的計算之中。然而&#xff0c;在一幅圖像之中&#xff0c;某種類型的像素并不是唯一的&#xff0c;因此我門常計算的是一類像素到另一類的最小距離&#xff0c;并…

My Spirit | “頂級復盤”

世界不會在意你的自尊&#xff0c; 人們看到的只是你的成就。 在你沒有成就之前&#xff0c; 切勿過分強調自尊。 ——菲茨杰拉德《了不起的蓋茨比》 目錄 My Spirit | “頂級復盤”00 | 日復盤01 | 周復盤2.1 周計劃2.2 周復盤2.3 下步計劃2.4 下步總結 02 | 月復盤2.1 本月目…

香橙派KunPengPro評測

一、引言 二、開箱 2.1、主要包含說明 1、充電器(贈typec-c線) 2、香橙派kunpengpro(已經帶裝好帶散熱器) 3、SD卡(32G)(已經帶裝好系統openEuler 22.03 (LTS-SP3)) (注意&#xff1a;上電接HDMI線可直接用&#xff0c;賬號&#xff1a;openEuler 密碼&#xff1a;openEuler)…

vue使用tailwindcss

安裝依賴 pnpm add -D tailwindcss postcss autoprefixer創建配置文件tailwind.config.js npx tailwindcss init在配置文件content中添加所有模板文件的路徑 /** type {import(tailwindcss).Config} */ export default {content: [./index.html, ./src/**/*.{vue,js,ts,jsx,…

【Linux】開發工具入門指南,輕松掌握你的開發利器

開發工具 1. 軟件包管理器yum1.1 軟件包安裝方式1.2 yum的"三板斧"1.3 yum的周邊 2. 開發工具3. 編輯器vim4. 編譯器gcc、g5. 項目自動化構建工具make、Makefile6. 進度條小程序7. 調試器gdb 1. 軟件包管理器yum 1.1 軟件包安裝方式 源代碼安裝&#xff1a;用戶手動…

微信小程序 npm構建+vant-weaap安裝

微信小程序&#xff1a;工具-npm構建 報錯 解決&#xff1a; 1、新建miniprogram文件后&#xff0c;直接進入到miniprogram目錄&#xff0c;再次執行下面兩個命令&#xff0c;然后再構建npm成功 npm init -y npm install express&#xff08;Node js后端Express開發&#xff…

智慧校園的機遇與挑戰

隨著5G、物聯網、大數據等技能的日漸老練&#xff0c;數字化正在滲透到各行各業中&#xff0c;為事務立異和價值增加供給支撐。在教育職業&#xff0c;運用智能化體系賦能教育辦理越來越受歡迎&#xff0c;教育信息化方針一再出臺&#xff0c;進一步加快了智慧校園落地的腳步。…

Linux - 文件管理高級 sed

3.處理字符 sed ① sed 默認情況下不會修改原文件內容 ② sed 是一種非交互式的編輯器 3.1 工作原理 將原文件一行一行的進行處理&#xff0c;取出一行&#xff0c;放入“模式空間進行處理”&#xff0c;處理完成之后將結果輸出到屏幕上&#xff0c;然后讀取下一行&#xf…

彭濤 | 2024年5月小結

5月份還是蠻有刺激的&#xff0c;做了蠻多的事情&#xff0c;但是沒賺到錢&#xff0c;真是一屯操作猛如虎&#xff0c;一看賬戶0.5。 就喜歡創業這種一天天累死累活還不賺錢的感覺&#xff0c;哈哈哈哈 老規矩簡單說下這個月的情況&#xff0c;如果對你有收獲就最好了。 游學丹…

測繪外業需要注意些什么?

在進行測繪外業時&#xff0c;需要注意的事項涉及多個方面&#xff0c;包括充分的準備工作、合理的設備選擇、精確的操作技巧以及細致的數據處理。下面將具體展開這些要點&#xff1a; 1. 充分準備 - 了解任務要求&#xff1a;在開始外業工作前&#xff0c;需要明確測繪的目…

VUE框架前置知識總結

一、前言 在學習vue框架中&#xff0c;總是有些知識不是很熟悉&#xff0c;又不想系統的學習JS&#xff0c;因為學習成本太大了&#xff0c;所以用到什么知識就學習什么知識。此文檔就用于記錄零散的知識點。主要是還是針對與ES6規范的JS知識點。 以下實驗環境都是在windows環…

頭歌頁面置換算法第2關:計算OPT算法缺頁率

2 任務:OPT算法 2.1 任務描述 設計OPT頁面置換算法模擬程序:從鍵盤輸入訪問串。計算OPT算法在不同內存頁框數時的缺頁數和缺頁率。要求程序模擬駐留集變化過程,即能模擬頁框裝入與釋放過程。 2.2任務要求 輸入串長度作為總頁框數目,補充程序完成OPT算法。 2.3算法思路 OPT算…

【Tlias智能學習輔助系統】04 部門管理 刪除 和 新增

Tlias智能學習輔助系統 04 部門管理 刪除 和 新增 刪除部門APIDeptController.javaDeptService.javaDeptServiceImpl.javaDeptMapper.java前端聯調 新增部門API有一步簡化DeptController.javaDeptService.javaDeptServiceImpl.javaDeptMapper.java前端聯調 刪除部門API 請求路徑…

31-ESP32-S3-WIFI篇-02 Event Group (事件標記組)

ESP32-S3-WIFI 事件標記組 介紹 在ESP32-S3的WiFi驅動程序中&#xff0c;事件標記組&#xff08;Event Group&#xff09;是一個非常重要的概念。它是FreeRTOS中的一種同步機制&#xff0c;用于在任務之間傳遞和同步事件。在WiFi驅動程序中&#xff0c;我們使用事件標記組來通…

Go 語言字符串及 strings 和 strconv 包

在 Go 語言編程中&#xff0c;字符串是最基本、最常用的數據類型之一。無論是處理用戶輸入、讀取文件內容&#xff0c;還是生成輸出&#xff0c;字符串操作無處不在。為了方便開發者對字符串進行各種操作&#xff0c;Go 語言提供了強大的 strings 包和 strconv 包。strings 包包…

Selenium+Java 環境搭建

selenium 介紹 Selenium 是 web 應用中基于 UI 的自動化測試框架&#xff0c;支持多平臺、多瀏覽器、多語言。 早期的 selenium RC 已經被現在的 webDriver 所替代&#xff0c;可以簡單的理解為selenium1.0webdriver 構成 現在的 Selenium2.0 。現在我們說起 selenium &#xf…

適合學生寫作業的臺燈有哪些?臺燈怎么選詳細攻略!

在數字化飛速發展的今天&#xff0c;孩子們的學習和生活越來越離不開電子屏幕。然而&#xff0c;長時間盯著屏幕&#xff0c;不僅容易讓眼睛感到疲勞&#xff0c;更是近視問題日益嚴重的元兇之一。每一位家長都希望孩子能擁有健康的視力&#xff0c;因此會為孩子挑選一臺護眼燈…