WebSocket解决方案(springboot 基于Redis发布订阅)

WebSocket        

        因为一般的请求都是HTTP请求(单向通信),HTTP是一个短连接(非持久化),且通信只能由客户端发起,HTTP协议做不到服务器主动向客户端推送消息。WebSocket确能很好的解决这个问题,服务端可以主动向客户端推送消息,客户端也可以主动向服务端发送消息,实现了服务端和客户端真正的平等

特点

1.全双工通信:允许服务器和客户端在同一连接上同时进行双向通信

2.持久连接:连接一旦建立,会一直保持打开状态,减少了每次连接建立和关闭的开销,使通信更加高效

3.低延迟:由于连接保持打开状态,WebSocket 通信具有较低的延迟,适用于实时性要求较高的应用

4.兼容性:代浏览器和大多数服务器支持 WebSocket

5.安全性:与其他网络通信协议一样,WebSocket 通信也需要一些安全性的考虑。可以使用加密协议(如 TLS)来保护数据在网络传输中的安全性

实战

1.添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2.创建配置类

创建配置类,并将其注入到Bean容器中

@Configuration
public class WebSocketConfig {/*** 注入ServerEndpointExporter,* 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
3.创建WebSocketServer类

创建WebSocketHandler类,并将其注入到Bean容器中

@ServerEndpoint("/websocket/{equipmentId}"),该注解用于配置建立WebSocket连接的路径,可以按需修改

@Component
@Slf4j
@ServerEndpoint("/websocket/{equipmentId}")
public class WebSocketHandler {private Session session;//concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。private static CopyOnWriteArraySet<WebSocketHandler> webSocketUtils = new CopyOnWriteArraySet<>();// 用来存在线连接数private static Map<String, Session> sessionPool = new HashMap<>();private static EquipmentService equipmentService = SpringContextHolder.getBean(EquipmentService.class);/*** 链接成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam(value = "equipmentId") String equipmentId) {try {this.session = session;webSocketUtils.add(this);sessionPool.put(equipmentId, session);sendOneMessage(equipmentId, "");equipmentService.onlineRecord(equipmentId,0);log.info("【websocket消息】有新的连接,总数为:" + webSocketUtils.size());} catch (Exception e) {e.printStackTrace();}}/*** 链接关闭调用的方法*/@OnClosepublic void onClose(@PathParam(value = "equipmentId") String equipmentId) {try {webSocketUtils.remove(this);equipmentService.onlineRecord(equipmentId,1);log.info("【websocket消息】连接断开,总数为:" + webSocketUtils.size());} catch (Exception e) {e.printStackTrace();}}/*** 收到客户端消息后调用的方法** @param message* @param*/@OnMessagepublic void onMessage(@PathParam(value = "equipmentId") String equipmentId, String message) {log.info("【websocket消息】收到客户端消息:" + message);sendOneMessage(equipmentId, message);}/*** 发送错误时的处理** @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("用户错误,原因:" + error.getMessage());error.printStackTrace();}/*** 推消息给前端** @param equipmentId* @param message* @return*/public static Runnable sendOneMessage(String equipmentId, Object message) {Session session = sessionPool.get(equipmentId);if (session != null && session.isOpen()) {try {log.info("【推给前端消息】 :" + message);//高并发下,防止session占用期间,被其他线程调用synchronized (session) {session.getBasicRemote().sendText(Objects.toString(message));}} catch (Exception e) {e.printStackTrace();}}return null;}

}

功能点:

1.处理异常: 与任何网络通信一样,WebSocket 连接可能会面临各种异常情况,如断开连接、网络问题等。WebSocket 服务器需要能够处理这些异常情况,进行适当的清理和处理。

2.消息处理: 一旦客户端连接成功,WebSocket 服务器需要处理客户端发送过来的消息。这可以在 WebSocket 端点中的方法上定义处理逻辑。服务器可以根据不同的业务需求处理不同类型的消息

3.WebSocket 服务器负责监听客户端的连接请求,一旦有客户端连接,服务器会创建一个 WebSocket 会话(Session)来管理这个连接。服务器需要能够维护这些连接,包括打开、关闭、保持心跳等操作。

4.WebSocket 服务器需要注册一个或多个 WebSocket 端点。每个端点对应一种处理逻辑,可以处理客户端发送过来的消息,以及向客户端发送消息。这些端点通过注解或配置来定义

因业务需求,常需要对获取的消息进行处理,websocket 不能注入( @Autowired ) service,解决办法:

private static EquipmentService equipmentService = SpringContextHolder.getBean(EquipmentService.class);

@Component
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {private static ApplicationContext applicationContext = null;/*** 取得存储在静态变量中的ApplicationContext.*/public static ApplicationContext getApplicationContext() {assertContextInjected();return applicationContext;}/*** 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.*/@SuppressWarnings("unchecked")public static <T> T getBean(String name) {assertContextInjected();return (T) applicationContext.getBean(name);}/*** 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.*/public static <T> T getBean(Class<T> requiredType) {assertContextInjected();return applicationContext.getBean(requiredType);}/*** 清除SpringContextHolder中的ApplicationContext为Null.*/public static void clearHolder() {applicationContext = null;}/*** 实现ApplicationContextAware接口, 注入Context到静态变量中.*/@Overridepublic void setApplicationContext(ApplicationContext appContext) {applicationContext = appContext;}/*** 实现DisposableBean接口, 在Context关闭时清理静态变量.*/@Overridepublic void destroy() throws Exception {SpringContextHolder.clearHolder();}/*** 检查ApplicationContext不为空.*/private static void assertContextInjected() {Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");}
}
4.测试

Redis 发布/订阅

特点

        发布/订阅是一种消息通信模式,其中发送者(发布者)发布消息,多个接收者(订阅者)订阅并接收这些消息。发布者和订阅者之间没有直接联系,消息由消息中间件(如 Redis)传递。

优点

        高性能:Redis 作为内存存储,具备极高的读写性能,能够快速处理发布和订阅消息

        简单易用:Redis 的发布/订阅接口简单,易于集成和使用

        实时性强:发布的消息会立即传递给所有订阅者,具备高实时性

缺点

        消息丢失:由于 Redis 是内存存储,如果 Redis 实例宕机,未处理的消息可能会丢失
        无法持久化:Redis 的发布/订阅模式不支持消息持久化,无法存储和检索历史消息
        订阅者不可控:发布者无法控制订阅者的数量和状态,无法保证所有订阅者都能接收到消息
        无确认机制:发布者无法确认消息是否被订阅者接收和处理

        Redis 的发布订阅功能并不可靠,如果我们需要保证消息的可靠性、包括确认、重试等要求,我们还是要选择MQ实现发布订阅

运用场景

        对于消息处理可靠性要求不强

        消息无需持久化

        消费能力无需通过增加消费方进行增强

        架构简单 中小型系统不希望应用过多中间件

发布订阅命令

SpringBoot整合

1.添加依赖

<dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

2.配置redis

# application.yml

spring:

        redis:

                host: localhost

                port: 6379

3.创建redis配置类
public void sendMsg(String key,Object msg){redisTemplate.convertAndSend(key,msg);
}

注意:

当发布消息时,订阅着输出消息,可能会出现乱码情况:

设置实例化对象

@Bean
public RedisTemplate redisTemplateInit() {//设置序列化Key的实例化对象redisTemplate.setKeySerializer(new StringRedisSerializer());//设置序列化Value的实例化对象redisTemplate.setValueSerializer(new StringRedisSerializer());return redisTemplate;
}
4.创建消息监听器
@Component
public class RedisMessageListener implements MessageListener {@Overridepublic void onMessage(Message message, byte[] pattern) {String messageStr = new String(message.getBody(),StandardCharsets.UTF_8);/*** 根据实际情况处理消息*/List<WebsocketRes> websocketRes = JSONArray.parseArray(messageStr, WebsocketRes.class);String equipmentId = "";List<WebsocketRes> websocketResList = new ArrayList<>();for(WebsocketRes res : websocketRes){equipmentId = res.getEquipmentId();res.setEquipmentId(null);websocketResList.add(res);}Gson gson = new Gson();String jsonString = gson.toJson(websocketResList);WebSocketHandler.sendOneMessage(equipmentId,jsonString);}
}
5.配置消息监听容器
@Configuration
public class RedisConfig {@Autowiredprivate RedisMessageListener redisMessageListener;@BeanRedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,MessageListenerAdapter listenerAdapter) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(connectionFactory);// 订阅一个或多个频道container.addMessageListener(listenerAdapter, new PatternTopic("my-topic"));return container;}@BeanMessageListenerAdapter listenerAdapter(RedisMessageListener redisMessageListener) {return new MessageListenerAdapter(redisMessageListener);}
}
6.发布消息
redisUtils.sendMsg("my-topic",jsonString);

websocket与发布/订阅结合

        并发过高时,websocket连接需单独部署,减缓压力;websocket将业务信息实时推送给前端,就用到了redis 发布订阅功能。

使用

socket消息推送时,把信息发布到redis中。socket服务订阅redis的消息,订阅成功后进行推送

1.在websocket服务中创建消息监听器(处理消息)

2.在websocket服务中创建消息监听容器

3.在业务服务中发布消息

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

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

相关文章

基于SpringBoot的漫画网站系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;B/S架构模式、Java技术 工具&#xff1a;Visual Studio、MySQL数据库开发工具 系统展示 首页 用户…

零基础学习MySQL---MySQL入门

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、什么是数据库 问&#xff1a;存储数据用文件就可以了&#xff0c;为什么还要弄个数据库呢&#xff1f; 这就不得不提…

如何在《语文新读写》期刊上发表论文?

如何在《语文新读写》期刊上发表论文&#xff1f; 《语文新读写》知网 省级G4 3版面4800字符数 24年10-11月 可版权页查稿 出刊晚5个月 《语文新读写》栏目&#xff1a;视点_本期特稿、视点_百家争鸣、探索_教材新探、探索_阅读风向、探索_写作杂谈、实践_教法学法、实践_教…

【python】OpenCV—Feature Detection and Matching

参考学习来自OpenCV基础&#xff08;23&#xff09;特征检测与匹配 文章目录 1 背景介绍2 Harris角点检测3 Shi-Tomasi角点检测4 Fast 角点检测5 BRIEF 特征描述子6 ORB(Oriented Fast and Rotated Brief) 特征描述子7 SIFT(Scale Invariant Feature Transform) 特征描述子8 SU…

Milvus ConnectionRefusedError: how to connect locally

题意&#xff1a;怎样在本地连接到 Milvus 数据库。连接 Milvus 数据库被拒绝的错误 问题背景&#xff1a; I am trying to run a RAG pipeline using haystack & Milvus. 我正在尝试使用 haystack 和 Milvus 运行一个 RAG&#xff08;检索增强型生成&#xff09;管道。 …

vue+element-ui简洁完美实现个人博客“​响石潭 ​”

目录 一、项目介绍 二、项目截图 1.项目结构图 2.首页 3.生活 ​编辑 4.文章详情 ​编辑 5.关于我 ​编辑 ​编辑 三、源码实现 1.项目依赖package.json 2.项目启动 3.首页源码 四、总结 一、项目介绍 本项目在线预览&#xff1a;点击访问 参考官网&#xff1…

腾讯 TRANSAGENTS: 多智能体翻译框架上线

之前介绍的由腾讯 AI 实验室搞得TRANSAGENTS&#xff08;多 Agent 系统&#xff0c;模拟现实翻译出版流程&#xff09;终于上线演示了&#xff01;提供了基于 GPT-4o 的免费试用, 暂时还是期货开源。

R语言fastshap包进行支持向量机shap可视化分析

1995年VAPINK 等人在统计学习理论的基础上提出了一种模式识别的新方法—支持向量机 。它根据有限的样本信息在模型的复杂性和学习能力之间寻求一种最佳折衷。 以期获得最好的泛化能力.支持向量机的理论基础决定了它最终求得的是全局最优值而不是局部极小值,从而也保证了它对未知…

[数据集][目标检测]围栏破损检测数据集VOC+YOLO格式1196张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1196 标注数量(xml文件个数)&#xff1a;1196 标注数量(txt文件个数)&#xff1a;1196 标注…

40V转5V,40V转3.3V,40V转3V使用什么降压芯片型号?

40V转5V,40V转3.3V,40V转3V使用什么降压芯片型号? # 40V转5V、3.3V、3V降压芯片&#xff1a;AH8820A的介绍与应用 在电子电路设计中&#xff0c;电压转换是一个常见的需求。特别是在需要将较高电压转换为较低电压以供微控制器、传感器和其他低电压设备使用时&#xff0c;降压…

力扣1685.有序数组中差绝对值之和

力扣1685.有序数组中差绝对值之和 记录左边之和 和 右边之和从左到右遍历每个元素 求res class Solution {public:vector<int> getSumAbsoluteDifferences(vector<int>& nums) {int n nums.size(),lsum 0,rsum accumulate(nums.begin(),nums.end(),0);ve…

匿名方法与Lambda表达式

知识集锦 一、lambda表达式介绍 无参数 () >{return "1";}; 等同于 string getnum(){ return "1"; } 有两个参数 (p1, p2) >{ return p1*p2;}; 等同于 int mul(p1, p2) { return p1*p2;}; lambda表达式可以捕获外部变量&#xff0c;并在其主体中使用…

怎么在电脑上录屏?跟着教程一步步操作

随着数字化时代的到来&#xff0c;电脑录屏已经成为一项必备技能。无论是录制游戏画面、制作教程视频&#xff0c;还是保存线上会议记录&#xff0c;录屏都能帮上大忙。可是怎么在电脑上录屏呢&#xff1f;本文将介绍两种在电脑上进行录屏的方法&#xff0c;这两种方法各有特点…

陶建辉当选 GDOS 全球数据库及开源峰会荣誉顾问

近日&#xff0c;第二十三届 GOPS 全球运维大会暨 XOps 技术创新峰会在北京正式召开。本次会议重点议题方向包括开源数据库落地思考、金融数据库自主可控、云原生时代下数据库、数据库智能运维、数据库安全与隐私、开源数据库与治理。大会深入探讨这些方向&#xff0c;促进了数…

宇宙第一大厂亚马逊云科技AWS人工智能/机器学习证书即将上线,一篇文章教你轻松拿下

据麦肯锡《在华企业如何填补AI人才缺口》研究表明&#xff0c;到2030年人工智能为中国带来的潜在价值有望超过1万亿美元&#xff0c;而随着各大企业进入人工智能化&#xff0c;对该领域的人才需求将从目前的100万增长到2030年的600万。然而到保守估计&#xff0c;到2030可以满足…

DevOps:开发与运维的无缝融合

目录 前言1. DevOps的起源与概念1.1 DevOps的起源1.2 DevOps的定义 2. DevOps的核心实践2.1 持续集成2.2 持续交付2.3 自动化 3. DevOps工具链3.1 版本控制系统3.2 持续集成工具3.3 配置管理工具3.4 容器化与编排工具3.5 监控和日志工具 4. DevOps的实际应用4.1 案例分析&#…

C语言实战 | 用户管理系统

近期推出的青少年防沉迷系统&#xff0c;采用统一运行模式和功能标准。在“青少年模式”下&#xff0c;未成年人的上网时段、时长、功能和浏览内容等方面都有明确的规范。防沉迷系统为青少年打开可控的网络空间。 01、综合案例 防沉迷系统的基础是需要一个用户管理系统管理用户…

C# 计算椭圆上任意一点坐标

已知圆心坐标 &#xff08;x0&#xff0c;y0&#xff09;&#xff0c;横轴 A&#xff08;长半轴&#xff09;&#xff0c;竖轴 B&#xff08;短半轴&#xff09;&#xff0c;角度 a&#xff0c;则圆边上点&#xff08;x&#xff0c;y&#xff09;的坐标为&#xff1a; 方法一 …

docker push 推送镜像到阿里云仓库

1.登陆阿里云 镜像服务&#xff0c;跟着指引操作就行 创建个人实例&#xff0c;创建命名空间、镜像仓库&#xff0c;绑定代码源头 2.将镜像推送到Registry $ docker login --username*** registry.cn-beijing.aliyuncs.com $ docker tag [ImageId] registry.cn-beijing.aliy…

Vue入门-如何创建一个Vue实例

创建一个一个Vue实例总共分为四步&#xff1a; 1.创建一个容器 2.引包&#xff1a;地址栏搜索v2.cn.vuejs.org这是vue2的官网地址&#xff0c;把2去掉就是vue3的官网地址&#xff0c;我们的包分为开发版本和生产版本&#xff0c;开发版本包含完整的警告和调试模式生产版本删除…