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.在业务服务中发布消息

相关文章:
WebSocket解决方案(springboot 基于Redis发布订阅)
WebSocket 因为一般的请求都是HTTP请求(单向通信),HTTP是一个短连接(非持久化),且通信只能由客户端发起,HTTP协议做不到服务器主动向客户端推送消息。WebSocket确能很好的解决这个问题&…...
如何优化网站SEO排名?
选择那些容易排名的关键词。使用工具找到那些竞争少但有流量的词语。其次,内部链接非常重要。通过合理的内部链接,可以提升各个页面的权重。 增加FAQ部分能帮助你捕捉更多的长尾关键词流量。争取出现在精选摘要的位置,可以直接提升你的曝光率…...
基于Java的音乐网站系统-计算机毕业设计源码01239
目 录 摘要 1 绪论 1.1 研究背景 1.2系统开发目标、意义 1.3研究内容 2 相关技术介绍 2.1 MySQL数据库 2.2 Java编程语言 2.3 SpringBoot框架介绍 3 系统需求分析与设计 3.1 可行性分析 3.1.1 技术可行性分析 3.1.2 经济可行性分析 3.1.3 法律可行性分析 3.2 需…...
云原生之容器编排实践-OpenEuler23.09在线安装Kubernetes与KubeSphere
背景 前几篇文章中介绍了如何将 ruoyi-cloud 项目部署到 Kubernetes 集群中,包括网关服务、认证服务和系统服务并且对全部服务采用 YAML 文件的方式来进行部署,这虽然有助于理解 K8S 组织管理资源的风格与底层机制,但是对于团队中不太熟悉命…...
Ubuntu 截图shutter,图像编辑 gimp,录屏kazam
1.截图: Shutter 安装shutter命令: sudo add-apt-repository ppa:shutter/ppasudo apt-get updatesudo apt-get install shutter 2.图片编辑:Gimp, Kolourpaint, Pinta gimp全名为:GNU Image Manipulation Program,…...
WSO2 products 文件上传漏洞(CVE-2022-29464)
前言 CVE-2022-29464 是一个影响多个 WSO2 产品的严重远程代码执行(RCE)漏洞。这些产品包括 WSO2 API Manager、WSO2 Identity Server 和 WSO2 Enterprise Integrator 等。由于用户输入验证不当,该漏洞允许未经身份验证的攻击者在服务器上上…...
YOLOv8改进 | 卷积模块 | SAConv可切换空洞卷积
秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡 专栏目录 :《YOLOv8改进有效…...
使用Python下载并合并HLS视频片段
下载和合并视频片段的实用方法 在日常工作中,我们经常会遇到需要从网上下载视频并将其合并成一个完整视频的需求。本文将介绍如何使用 Python 下载多个视频片段,并使用 ffmpeg 将这些片段合并成一个完整的视频文件。以下是具体步骤和代码实现。 完整代…...
常见的九种二极管
常见的九种二极管 文章目录 常见的九种二极管1、普通二极管2、光电二极管(LED)3、变容二级管4、发光二极管5、恒流二极管6、快恢复二极管(FRD)7、肖特基二极管8、瞬态电压抑制二极管(TVS)9、齐纳二极管(稳压࿰…...
竞赛选题 python的搜索引擎系统设计与实现
0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 python的搜索引擎系统设计与实现 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分工作量:5分创新点:3分 该项目较为新颖ÿ…...
大模型技术方向夏令营1期-对话分角色要素提取挑战赛
#AI夏令营 #Datawhale #夏令营 一、 baseline 跑通 Baseline 本身挑战性有限,关键是熟悉 LLM-centric 相关任务 coding 层面的流程方法,比如: 大模型 API(这里为科大讯飞 Spark)调用token消耗的理解如何调用大模型实现针对给定…...
类和对象(封装、继承、多态、友元)
c面相对象的三大特性为:封装、继承、多态 c 认为万事万物都皆为对象,对象上有其属性和行为 一、类和对象(封装) (一)封装的意义 封装是c面相对象的三大特性之一 封装的意义: 将属性和行为…...
关于Yolov8我踩过的那些坑
按照报错频次梳理: 致命反斜杠‘\’ 调用模型时,我喜欢‘copy relative location’,然后win系统默认反斜杠! 就导致路径读取错误!各种报错!! debug到崩溃然后发现是斜杠的问题,本吗喽…...
Linux——shell原理和文件权限
1.shell原理 在我们使用云服务器时,需要通过shell进行使用,而shell则是一种外壳程序。 我们提到过,大部分的指令实际上就是文件,当用户需要执行某种功能时,由于用户不擅长和操作系统直接交互(操作复杂&…...
网络工程师需要熟悉Docker吗?我觉得不需要精通,但是得懂基础
你好,这里是网络技术联盟站,我是瑞哥。 Docker,这个字眼大家不陌生吧,不过作为网络工程师可能平时接触不到,如果在看文章的是运维人员,那么70%以上的运维人员都会跟Docker打交道。即使网工用不到ÿ…...
c++初级-2-引用
文章目录 引用一、引用的定义二、引用做函数参数三、引用作为返回对象四、引用的本质五、常量引用 引用 即给一个变量起别名。 一、引用的定义 int a 10;//引用int& b a;cout << "a " << a << endl;cout << "b " <&l…...
如何清理电脑内存?让电脑运行如飞!
电脑内存(RAM)的清理对于维持系统的流畅运行至关重要。随着使用时间的增加,系统内存会被各种应用程序和后台进程占用,导致系统响应变慢,甚至出现卡顿现象。通过有效地清理内存,可以提升电脑的性能ÿ…...
[数据集][目标检测]人员状态跑睡抽烟打电话跌倒检测数据集4943张5类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):4943 标注数量(xml文件个数):4943 标注数量(txt文件个数):4943 标注…...
Java8 - Stream API 处理集合数据
Java 8的Stream API提供了一种功能强大的方式来处理集合数据,以函数式和声明式的方式进行操作。Stream API允许您对元素集合执行操作,如过滤、映射和归约,以简洁高效的方式进行处理。 下面是Java 8 Stream API的一些关键特性和概念ÿ…...
漫步5G-A City,一份独属于上海的浪漫
作家亨利詹姆斯曾写道,“城市漫步,让我接触到了这个世界上最好的东西”。 用漫无目的地行走,来体验和观察一座城市,上海凭借丰富多元的文化特质,成为citywalk这种浪漫生活方式的流行地。 无论你是漫步在美术馆、画廊林…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...
算法打卡第18天
从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。 示例 1: 输入:inorder [9,3,15,20,7…...
恶补电源:1.电桥
一、元器件的选择 搜索并选择电桥,再multisim中选择FWB,就有各种型号的电桥: 电桥是用来干嘛的呢? 它是一个由四个二极管搭成的“桥梁”形状的电路,用来把交流电(AC)变成直流电(DC)。…...
Python常用模块:time、os、shutil与flask初探
一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...
机器学习的数学基础:线性模型
线性模型 线性模型的基本形式为: f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法,得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...
