基于Spring的消息推送实战(Websocket和前端轮询实现)

基于Spring的消息推送实战(Websocket和前端轮询实现)

        本文介绍了基于Spring的消息推送实现方法,主要介绍了websocket实时消息推送方法(ServerEndpoint方式实现),以及前端客户端轮询方式的消息推送。

一、消息推送

       常见的消息推送方式有轮询、websocket、jpush等。

       传统http协议需要客户端发起请求,不能服务端进行推送,且建立tcp连接需要多次握手(tcp三次握手建立连接,四次握手关闭连接),是单工通道,请求头也需要大量信息,造成资源浪费。

        Websocket仅需一次握手就可以在客户端和服务器之间建立长时间、全双工、双向交互的连接通道,且请求头报文可以压缩传输,减轻资源浪费,支持服务器主动向客户端发送消息。

二、应用场景

通常用在实时同步性要求高的场景,如:

1、实时数据监控和推送:WebSocket可以用于实时监控和推送数据,例如股票行情,实时交通信息、天气、预警等。服务器可以将最新的数据实时推送给客户端,使用户能够实时获取并展示最新的数据。

2、即时聊天:WebSocket可以实现实时的消息传递,使得即时聊天应用能够实时地将消息推送给在线用户,实现实时的聊天体验。

3、多人协作应用:WebSocket可以用于多人协作应用程序,例如实时协作编辑器、白板应用等。多个用户可以在同一文档或画布上实时协作、彼此之间的更改和操作可以实时同步。

三、WebSocket实现

1、两种实现方法

Websocket可以通过ServerEndpointExporter和registerWebSocketHandlers两种不同的方式实现。

使用@ServerEndpoint注解来定义WebSocket端点;

registerWebSocketHandlers可以指定一个WebSocketHandler实例添加自定义的拦截器或使用Spring的注解驱动(如@MessageMapping)

2、依赖引入

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

3、WebSocket处理器方法及端点定义

@ServerEndpoint指定端点地址;

onOpen、onMssage、onError、onClose为接受信息不同状态的处理方法,可向前端客户端发送消息
 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.Semaphore;/*** websocket 消息处理**/
@Component
@ServerEndpoint("/websocket/message")
public class WebSocketServer
{/*** WebSocketServer 日志控制器*/private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class);/*** 默认最多允许同时在线人数100*/public static int socketMaxOnlineCount = 100;private static Semaphore socketSemaphore = new Semaphore(socketMaxOnlineCount);/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session) throws Exception{boolean semaphoreFlag = false;// 尝试获取信号量semaphoreFlag = SemaphoreUtils.tryAcquire(socketSemaphore);if (!semaphoreFlag){// 未获取到信号量LOGGER.error("\n 当前在线人数超过限制数- {}", socketMaxOnlineCount);WebSocketUsers.sendMessageToUserByText(session, "当前在线人数超过限制数:" + socketMaxOnlineCount);session.close();}else{// 添加用户WebSocketUsers.put(session.getId(), session);LOGGER.info("\n 建立连接 - {}", session.getId());LOGGER.info("\n 当前人数 - {}", WebSocketUsers.getUsers().size());WebSocketUsers.sendMessageToUserByText(session, "连接成功");}}/*** 连接关闭时处理*/@OnClosepublic void onClose(Session session){LOGGER.info("\n 关闭连接 - {}", session);// 移除用户WebSocketUsers.remove(session.getId());// 获取到信号量则需释放SemaphoreUtils.release(socketSemaphore);}/*** 抛出异常时处理*/@OnErrorpublic void onError(Session session, Throwable exception) throws Exception{if (session.isOpen()){// 关闭连接session.close();}String sessionId = session.getId();LOGGER.info("\n 连接异常 - {}", sessionId);LOGGER.info("\n 异常信息 - {}", exception);// 移出用户WebSocketUsers.remove(sessionId);// 获取到信号量则需释放SemaphoreUtils.release(socketSemaphore);}/*** 服务器接收到客户端消息时调用的方法*/@OnMessagepublic void onMessage(String message, Session session){String msg = message.replace("你", "我").replace("吗", "");WebSocketUsers.sendMessageToUserByText(session, msg);}
}

4、处理子方法


 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.websocket.Session;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;/*** websocket 客户端用户集** @author Inspur*/
public class WebSocketUsers
{/*** WebSocketUsers 日志控制器*/private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUsers.class);/*** 用户集*/private static Map<String, Session> USERS = new ConcurrentHashMap<String, Session>();/*** 存储用户** @param key 唯一键* @param session 用户信息*/public static void put(String key, Session session){USERS.put(key, session);}/*** 移除用户** @param session 用户信息** @return 移除结果*/public static boolean remove(Session session){String key = null;boolean flag = USERS.containsValue(session);if (flag){Set<Map.Entry<String, Session>> entries = USERS.entrySet();for (Map.Entry<String, Session> entry : entries){Session value = entry.getValue();if (value.equals(session)){key = entry.getKey();break;}}}else{return true;}return remove(key);}/*** 移出用户** @param key 键*/public static boolean remove(String key){LOGGER.info("\n 正在移出用户 - {}", key);Session remove = USERS.remove(key);if (remove != null){boolean containsValue = USERS.containsValue(remove);LOGGER.info("\n 移出结果 - {}", containsValue ? "失败" : "成功");return containsValue;}else{return true;}}/*** 获取在线用户列表** @return 返回用户集合*/public static Map<String, Session> getUsers(){return USERS;}/*** 群发消息文本消息** @param message 消息内容*/public static void sendMessageToUsersByText(String message){Collection<Session> values = USERS.values();for (Session value : values){sendMessageToUserByText(value, message);}}/*** 发送文本消息** @param userName 自己的用户名* @param message 消息内容*/public static void sendMessageToUserByText(Session session, String message){if (session != null){try{session.getBasicRemote().sendText(message);}catch (IOException e){LOGGER.error("\n[发送消息异常]", e);}}else{LOGGER.info("\n[你已离线]");}}
}

5、信号量限流工具类

Semaphore 就是一个信号量,它的作用是限制某段代码块的并发数。Semaphore有一个构造函数,可以传入一个 int 型整数 n,表示某段代码最多只有 n 个线程可以访问,如果超出了 n,那么请等待,等到某个线程执行完毕这段代码块,下一个线程再进入。由此可以看出如果 Semaphore 构造函数中


 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.concurrent.Semaphore;/*** 信号量相关处理** @author Inspur*/
public class SemaphoreUtils
{/*** SemaphoreUtils 日志控制器*/private static final Logger LOGGER = LoggerFactory.getLogger(SemaphoreUtils.class);/*** 获取信号量** @param semaphore* @return*/public static boolean tryAcquire(Semaphore semaphore){boolean flag = false;try{flag = semaphore.tryAcquire();}catch (Exception e){LOGGER.error("获取信号量异常", e);}return flag;}/*** 释放信号量** @param semaphore*/public static void release(Semaphore semaphore){try{semaphore.release();}catch (Exception e){LOGGER.error("释放信号量异常", e);}}
}

6、开启websocket(WebSocket.java)

package com.inspur.framework.websocket;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** websocket 配置** @author Inspur*/
@Configuration
public class WebSocketConfig
{@Beanpublic ServerEndpointExporter serverEndpointExporter(){return new ServerEndpointExporter();}
}

7、SpringSecurity开启白名单

public class SecurityConfig extends WebSecurityConfigurerAdapter {protected void configure(HttpSecurity httpSecurity) throws Exception {.antMatchers("/websocket/**").permitAll()}}

8、后台推送消息

后台业务中,服务器向前端推送消息

WebSocketUsers.sendMessageToUsersByText("message消息示例!");

9、前台接收消息

(1)websocket地址配置

和@ServerEndpoint配置的地址一致

文件.env.development

# websocket 后台地址
VUE_APP_WEBSOCKET_URL = 'ws://127.0.0.1:8290/pro-service/websocket/message'

(2)前台依赖

文件package.json

"reconnecting-websocket": "^4.4.0",

(3)全局地址

Utils/webSocket.js’

export default {connectUrl: process.env.NODE_ENV === 'development' ? process.env.VUE_APP_WEBSOCKET_URL:`${location.protocol === 'https' ? 'wss' : 'ws'}://${location.host}`+process.env.VUE_APP_WEBSOCKET_URL
}

(4)接受消息

在Navbar.vue里,该组件在任何页面都存在

import {Notification} from "element-ui";
import moment from "moment";
import Vue from "vue";
import Router from "vue-router";import WebSocket from "@/utils/webSocket";mounted() {this.ws = new ReconnectingWebSocket(WebSocket.connectUrl);
console.log(WebSocket.connectUrl)
const self = this;
this.ws.onopen = function (event) {
};
this.ws.onmessage = function (event) {if (event.data != null && event.data != "连接成功") {Notification.success(event.data)}
};
this.ws.onclose = function (event) {// Notification.info("已经关闭连接")
};}

四、轮询方案

前端采用定时器+navbar推送展示消息

1、Interval定时器轮询

navbar的mounted方法,添加定时器

import {Notification} from "element-ui";mounted() {// 每隔1小时执行一次this.timer = setInterval(()=>{// 向后台服务器请求方法//eg:this.$store.dispatch('Method1')},1000 * 60 * 60)
},
beforeDestroy() {//清除定时器clearInterval(this.timer);
},

2、请求方法设置

可以直接在定时器里请求,也可异步请求

import { Notification } from 'element-ui'State:{maintainOrderToApproveCount: 0,
maintainOrderToApproveList: [],}mutations: {SET_MAINTAIN_ORDER_TO_APPROVE_LIST: (state, maintainOrderToApproveList) => {state.maintainOrderToApproveList = maintainOrderToApproveList
},SET_MAINTAIN_ORDER_TO_APPROVE_COUNT: (state, maintainOrderToApproveCount) => {state.maintainOrderToApproveCount = maintainOrderToApproveCount
},}actions: {// 请求方法
Method1({commit}) {return new Promise((resolve, reject) => {getMyApprovalOrderList().then(res => {if (res.code === 200) {commit('SET_MAINTAIN_ORDER_TO_APPROVE_COUNT', res.data.length)commit('SET_MAINTAIN_ORDER_TO_APPROVE_LIST', res.data.length)Notification.warning({title: '您有待审批的工单未完成',message: '尊敬的用户您好,您有'+res.data.length+'条工单待审批处理,请及时审批',duration: 5000})}resolve()}).catch(error => {reject(error)})})
},}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/51956.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Qt】 QComboBox | QSpinBox

文章目录 QComboBox —— 下拉框QComboBox 属性核心方法核心信号QComboBox 使用 QSpinBox —— 微调框QSpinBox 属性核心信号QSpinBox 使用 QComboBox —— 下拉框 QComboBox 属性 QComboBox —— 表示下拉框 currentText ——当前选中的文本 currentindex ——当前选中的条…

如何在虚拟机中安装部署K8S?

教程参考&#xff1a;centos7安装k8s 1.28版本&#xff0c;基于科学-CSDN博客 环境准备&#xff1a; 准备三台机器&#xff0c;都做以下操作&#xff0c;或者只准备一个机器&#xff0c;最后再克隆两台。 yum&#xff1a; 换源&#xff0c;这是阿里云的源 sudo wget -O /etc…

详解Asp.Net Core管道模型中的五种过滤器的适用场景与用法

1. 前言 在 ASP.NET Core 中&#xff0c;过滤器是一种用于对请求管道进行前置或后置处理的组件。它们可以在请求处理的不同阶段干预和修改请求和响应&#xff0c;以实现一些通用的处理逻辑或功能增强。 ASP.NET Core 的管道模型由多个中间件组成&#xff0c;而过滤器是这个模…

kafka及异步通知文章上下架

1)自媒体文章上下架 需求分析 2)kafka概述 消息中间件对比 特 性 ActiveMQ RabbitMQ RocketMQ Kafka 开 发 语 言 java erlang java scala 单 机 吞 吐 量 万级 万级 10万级 100万级 时 效 性 ms us ms ms级以内 可 用 性 高&#xff08;主从&#xff0…

如何从 Bak 文件中恢复 SQL数据库?(3种方法)

如何从 .bak 文件恢复 SQL数据库&#xff1f; 在数据库管理和维护过程中&#xff0c;数据的安全性和完整性至关重要。备份文件&#xff08;.bak 文件&#xff09;是 SQL Server 中常用的数据库备份格式&#xff0c;它包含了数据库的完整副本&#xff0c;用于在数据丢失、系统故…

flutter与原生怎么交互的

Flutter 与原生平台(如 Android 和 iOS)之间的交互可以通过**平台通道(Platform Channels)**实现。这允许你在 Flutter 应用中调用原生代码,或者从原生代码中调用 Flutter 代码。这种机制使得你可以利用原生平台提供的特性和 API,同时保持大部分应用代码在 Flutter 中。 …

4. 第一个3D案例—创建3D场景

入门Three.js的第一步&#xff0c;就是认识场景Scene、相机Camera、渲染器Renderer三个基本概念&#xff0c;接下来&#xff0c;咱们通过三小节课&#xff0c;大家演示“第一个3D案例”完成实现过程。 学习建议&#xff1a;只要你能把第一个3D案例搞明白&#xff0c;后面学习就…

二百六十、Java——采集Kafka数据,解析成一条条数据,写入另一Kafka中(复杂JSON)

一、目的 由于部分数据类型频率为1s&#xff0c;从而数据规模特别大&#xff0c;因此完整的JSON放在Hive中解析起来&#xff0c;尤其是在单机环境下&#xff0c;效率特别慢&#xff0c;无法满足业务需求。 而Flume的拦截器并不能很好的转换数据&#xff0c;因为只能采用Java方…

SEO之网站结构优化(十四-内部链接及权重分配3)

初创企业搭建网站的朋友看1号文章&#xff1b;想学习云计算&#xff0c;怎么入门看2号文章谢谢支持&#xff1a; 1、我给不会敲代码又想搭建网站的人建议 2、“新手上云”能够为你开启探索云世界的第一步 博客&#xff1a;阿幸SEO~探索搜索排名之道 7、锚文字分布及变化 前面…

新手c语言讲解及题目分享(十四)--函数专项练习(一)

目录 前言 一.函数的定义 1.函数定义包括的内容&#xff1a; Ⅰ.指定函数类别 Ⅱ.指定函数类型 Ⅲ.指定函数名 Ⅳ.指定函数的参数名称和类型 Ⅴ.指定函数的函数体 2.函数定义的一般形式&#xff1a; Ⅰ.有参函数的定义形式&#xff1a; Ⅱ.无参函数的定义形式&#x…

C语言从头学55——学习头文件errno.h、float.h

1、头文件 errno.h 中的变量 errno 的使用 在 errno.h 定义了一个 int 类型的变量 errno&#xff08;错误码&#xff09;&#xff0c;如果发现这个变量出现非零值&#xff0c;表示已经执行的函数发生了错误。这个变量一般多用于检查数学函数运算过程中发生的错误。 …

部署 Web 项目到 Linux,可以使他人也访问项目的方法

目录 一、环境配置 二、建构项目并打包 三、上传Jar包到服务器, 并运行 3.1 上传Jar包 3.2 运行 jar 包 3.3 开放端口号 四、其他问题 4.1 运行异常问题 4.2 杀掉进程 五、总结 一、环境配置 如果本地项目是SpringBoot项目&#xff0c;使用的数据库是MySQL&#xff…

ES6 类-总结

我们现在用一段代码&#xff0c; 在注释中总结所有关于JavaScript类的所有用法 class Student extends Person {//这里的Student是子类&#xff0c;Person是父类&#xff0c;extends是实现类之间的继承&#xff0c;它可以自动设置原型university 家里蹲大学; //公共字段(类似…

APP 数据抓取 - Charles 抓包工具的使用(Charles 端口配置、CA 证书配置、Charles Android 模拟器配置)

前言说明 此文章是我在学习 Charles APP 抓包时编写&#xff0c;内容都是亲测有效&#xff0c;文章内容也有参考其他人&#xff0c;参考文章如下&#xff1a; Android 手机使用 charles 抓 https 请求&#xff08;保姆级教程&#xff09;网易 mumu 模拟器安装下载 charles 的…

计算机网络(八股文)

这里写目录标题 计算机网络一、网络分层模型1. TCP/IP四层架构和OSI七层架构⭐️⭐️⭐️⭐️⭐️2. 为什么网络要分层&#xff1f;⭐️⭐️⭐️3. 各层都有那些协议&#xff1f;⭐️⭐️⭐️⭐️ 二、HTTP【重要】1. http状态码&#xff1f;⭐️⭐️⭐️2. 从输入URL到页面展示…

XSLT 实例:掌握 XML 转换的艺术

XSLT 实例&#xff1a;掌握 XML 转换的艺术 引言 XSLT&#xff08;可扩展样式表语言转换&#xff09;是一种强大的工具&#xff0c;用于将 XML&#xff08;可扩展标记语言&#xff09;文档转换为其他格式&#xff0c;如 HTML、PDF 或纯文本。在本文中&#xff0c;我们将通过一…

从Vuex 到 Pinia,Vue 状态管理的进化

Vue.js,一个轻量级且易于上手的 JavaScript 框架,已经在全球范围内获得了广泛的应用。 Vue.js 的状态管理库 Vuex,也为开发者提供了一个统一的状态管理方案。然而,随著 Vue.js 的发展和进化,我们看到了一个新的状态管理库的诞生 — Pinia。在这篇文章中,我们将探讨 Vuex…

2024年9月3日嵌入式学习

数据结构 1定义 一组用来保存一种或者多种特定关系的数据的集合&#xff08;组织和存储数据&#xff09; 程序的设计&#xff1a;将现实中大量而复杂的问题以特定的数据类型和特定的存储结构存储在内存中&#xff0c; 并在此基础上实现某个特定的功能的操作&am…

Springboot集成WebSocket客户端,发送消息并监测心跳

jar包&#xff08;主要jar包&#xff09; <dependency><groupId>org.java-websocket</groupId><artifactId>Java-WebSocket</artifactId><version>1.5.7</version></dependency>服务类 import cn.hutool.json.JSONUtil; impor…

「Python程序设计」条件控制:if-elif-else语句

我们在进行程序设计的过程中&#xff0c;基本上遵循的过程是&#xff0c;找出变量和常量&#xff0c;通过python编程语言&#xff0c;设置变量和常量&#xff0c;以及考虑是否需要赋予初始值。 设计变量和常量&#xff0c;其实就是为了模拟和计算我们的现实世界中&#xff0c;…