分布式websocket实时通讯的session共享问题

目录

1.需求

2.前置条件和要求

3.方案分析

3.1.方案1:session共享存储到redis数据库

3.2.方案2:采用mongo生命周期的AbstractMongoEventListener

3.3.方案3:引入redis等MQ组件,发送广播消息

3.4.方案4:采用change stream方式同步数据

4.基于change stream方式实现分布式下websocket的消息推送

4.1.pom.xml引入mongodb启动器

4.2.配置类增加自定义bean

4.3.实现监听中的回调函数

5.mongodb的单机模式下也可采用AbstractMongoEventListener方式

6.文档推荐


1.需求

就一句话:在多个springboot进程和多节点Mongo副本集集群的基础上,解决websocket的session共享问题。

2.前置条件和要求

1.多节点的Mongo副本集集群

2.单一的springboot服务的多个副本,也就是多个相同的springboot进程

3.要求实时性高,拒绝定时器(主要是有窗口期,消耗的资源伤不起),无论是前端定时还是后端定时。

3.方案分析

3.1.方案1:session共享存储到redis数据库

方案不可行

原因:

Session无法采用Redis进行存储, 因为不能对Session进行序列化

那为什么Session无法进行序列化呢?

因为其中包含了一些不可序列化的对象,比如底层网络连接和线程相关的信息。这些信息无法被简单地序列化和反序列化,因此直接对 WebSocket session 进行序列化会导致一些问题,比如无法正确地恢复网络连接状态和线程状态。

3.2.方案2:采用mongo生命周期的AbstractMongoEventListener

方案不可行

原因:

AbstractMongoEventListener内部采用的是利用MongoDB的事件通知机制,在文档操作时触发相应的事件,然后监听器将收到事件通知并执行预定义的操作。

本质还是在Spring应用程序中注册监听器,从而实现对MongoDB文档生命周期事件的监听和处理,是基于单线程的,在多个springboot线程下,不能实现数据共享。

3.3.方案3:引入redis等MQ组件,发送广播消息

原理:

多进程同时监听消费MQ,当有消息需要推送的时候,发送MQ广播消息,每个springboot进程都能收到广播消息,然后检查进程缓存中的websocket session连接信息,推送消息。

方案可行

缺点:

1.需要引入第三方组件,增加代码复杂性

2.高可用环境下,增加运维难度,需要维护搭建MQ集群

3.4.方案4:采用change stream方式同步数据

MongoDB 从 3.6 版本开始提供订阅数据变更的功能,但仅限于mongo集群,包括mongo副本集模式和mongo分片模式

原理:

基于 oplog 实现的,提供推送实时增量的推送功能。它在 oplog 上开启一个 tailable cursor 来追踪所有复制集上的变更操作,最终调用应用中定义的回调函数

方案可行

4.基于change stream方式实现分布式下websocket的消息推送

4.1.pom.xml引入mongodb启动器

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

4.2.配置类增加自定义bean

import com.mongodb.client.model.changestream.FullDocument;
import com.xxx.listener.AlarmMessageListener;
import com.xxx.util.AESUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.messaging.ChangeStreamRequest;
import org.springframework.data.mongodb.core.messaging.DefaultMessageListenerContainer;
import org.springframework.data.mongodb.core.messaging.MessageListener;
import org.springframework.data.mongodb.core.messaging.MessageListenerContainer;
import org.springframework.data.mongodb.core.query.Criteria;import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@Configuration
public class MongoDBConfig {@Beanpublic MongoTemplate mongoTemplate() throws Exception {return new MongoTemplate(mongoDatabaseFactory());}@Autowiredpublic ApplicationEventPublisher eventPublisher;@Bean@ConditionalOnProperty(name = "db.is-stand-alone", havingValue = "false")public MessageListenerContainer alarmMessageListenerContainer(MongoTemplate mongoTemplate) {AlarmMessageListener messageListener = new AlarmMessageListener(mongoTemplate, eventPublisher);return customMessageListenerContainer("alarm", mongoTemplate, messageListener);}private MessageListenerContainer customMessageListenerContainer(String collectionName,MongoTemplate template, MessageListener messageListener) {MessageListenerContainer messageListenerContainer = new DefaultMessageListenerContainer(template, Executors.newFixedThreadPool(5)) {@Overridepublic boolean isAutoStartup() {return true;}};ChangeStreamRequest<Document> request = ChangeStreamRequest.builder(messageListener).collection(collectionName) // 需要监听的集合名// 过滤需要监听的操作类型,可以根据需求指定过滤条件.filter(Aggregation.newAggregation(Aggregation.match(Criteria.where("operationType").in("insert", "update"))))// 不设置时,文档更新时,只会发送变更字段的信息,设置UPDATE_LOOKUP会返回文档的全部信息.fullDocumentLookup(FullDocument.UPDATE_LOOKUP).build();messageListenerContainer.register(request, Document.class);return messageListenerContainer;}
}

4.3.实现监听中的回调函数

import com.mongodb.client.model.changestream.ChangeStreamDocument;
import com.xxx.alarm.model.Alarm;
import com.xxx.notification.event.AlarmEventPublisher;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.bson.Document;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.messaging.Message;
import org.springframework.data.mongodb.core.messaging.MessageListener;@Slf4j
public class AlarmMessageListener implements MessageListener<ChangeStreamDocument<Document>, Object> {private MongoTemplate mongoTemplate;private ApplicationEventPublisher eventPublisher;public AlarmMessageListener(MongoTemplate mongoTemplate, ApplicationEventPublisher eventPublisher) {this.mongoTemplate = mongoTemplate;this.eventPublisher = eventPublisher;}@Overridepublic void onMessage(Message<ChangeStreamDocument<Document>, Object> message) {ChangeStreamDocument<Document> changeStreamDocument = message.getRaw();log.info("changestream操作为 :" + changeStreamDocument);Document document = changeStreamDocument.getFullDocument();Alarm alarm = mongoTemplate.getConverter().read(Alarm.class, document);if(alarm == null) return;//实时消息推送AlarmEventPublisher.publishDeviceAlarmEvent(alarm, eventPublisher);}
}

5.mongodb的单机模式下也可采用AbstractMongoEventListener方式

增加自定义listener监听器即可

import com.xxx.alarm.model.Alarm;
import com.xxx.notification.event.AlarmEventPublisher;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;/*** 告警监听*/
@Slf4j
@Component
public class AlarmMongoDataEventListener extends AbstractMongoEventListener<Alarm> {private ApplicationEventPublisher eventPublisher;public AlarmMongoDataEventListener(ApplicationEventPublisher eventPublisher) {this.eventPublisher = eventPublisher;}@Overridepublic void onBeforeConvert(BeforeConvertEvent<Alarm> alarmEvent) {Alarm alarm =alarmEvent.getSource();if(alarm == null) return;String notificationType = alarm.getNotificationType();if(StringUtils.isBlank(notificationType)) return;log.info("Before Convert alarm Event: " + alarm);//实时消息推送AlarmEventPublisher.publishDeviceAlarmEvent(alarm, eventPublisher);}
}

6.文档推荐

mongodb生命周期

AbstractMongoEventListener (Spring Data MongoDB 4.2.5 API)

Lifecycle Events :: Spring Data MongoDB

mongodb集群的change streams

Change Streams - MongoDB Manual v7.0

ChangeStreams 使用及原理(二)|学习笔记-阿里云开发者社区

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

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

相关文章

AI4Science

AI4Science 文章目录 AI4ScienceMicroSoft AI4Science其它 微软研究院刘铁岩&#xff1a;AI for Science&#xff0c;追求人类智能最光辉的一面&#xff5c;MEET2023 &#xff08;17min&#xff09; https://www.bilibili.com/video/BV1bs4y1W7rW/AI Forum 2023 | AI4Science: …

html使用<el-table-column />时不能正常渲染出页面

一、bug开始了 <el-table-column prop"date" label"Date" width"180"/>当使用上述代码进行html渲染时&#xff0c;发现没有正常渲染出想象中的视图&#xff0c;乱七八糟地。 二、bug解决 在IDE中报了如下警告&#xff1a; Empty tag do…

EasyCVR智慧校园建设中的关键技术:视频汇聚智能管理系统应用

一、引言 随着信息技术的迅猛发展&#xff0c;智慧校园作为教育信息化建设的重要组成部分&#xff0c;对于提升校园安全、教学效率和管理水平具有重要意义。本文旨在介绍智慧校园视频管理系统的架构设计&#xff0c;为构建高效、智能的校园视频监控系统提供参考。 二、系统整…

机器学习之sklearn基础教程(第三篇:模型选择和评估)

机器学习之sklearn基础教程&#xff08;第三篇&#xff1a;模型选择和评估&#xff09; 1. 模型选择 在机器学习任务中&#xff0c;选择合适的模型是非常重要的。不同的模型适用于不同的问题类型和数据特征。 在模型选择过程中&#xff0c;有几个常用的方法和原则&#xff1a;…

Vitis HLS 学习笔记--资源绑定-使用URAM(1)

目录 1. 简介 2. 代码分析 2.1 存储器代码 2.2 Implementation报告 2.3 存储器类型指定 2.4 存储器初始化 3. 总结 1. 简介 在博文《Vitis HLS 学习笔记--资源绑定-使用URAM-CSDN博客》中&#xff0c;介绍了如何在Vitis HLS环境下设计一个简易的存储器模型。 通过以下…

gin自定义验证器+中文翻译

gin自定义验证器中文翻译 1、说明2、global.go3、validator.go4、eg&#xff1a;main.go5、调用接口测试 1、说明 gin官网自定义验证器给的例子相对比较简单&#xff0c;主要是语法级别&#xff0c;便于入门学习&#xff0c;并且没有给出翻译相关的处理&#xff0c;因此在这里记…

红黑树底层封装map、set C++

目录 一、框架思考 三个问题 问题1的解决 问题2的解决&#xff1a; 问题3的解决&#xff1a; 二、泛型编程 1、仿函数的泛型编程 2、迭代器的泛型编程 3、typename&#xff1a; 4、/--重载 三、原码 红黑树 map set 一、框架思考 map和set都是使用红黑树底层&…

超级好用的C++实用库之MD5信息摘要算法

&#x1f4a1; 需要该C实用库源码的大佬们&#xff0c;可搜索微信公众号“希望睿智”。添加关注后&#xff0c;输入消息“超级好用的C实用库”&#xff0c;即可获得源码的下载链接。 概述 MD5信息摘要算法是一种广泛使用的密码散列函数&#xff0c;由Ronald L. Rivest在1991年设…

【DevOps】Linux 与虚拟局域网 (VLAN) 详解

目录 一、什么是VLAN&#xff1f; 二、VLAN的工作原理 三、Linux中的VLAN支持 四、内核模块 五、用户空间工具 六、创建VLAN 七、配置VLAN 八、管理VLAN 九、VLAN的应用 1、 网络隔离 2、网络管理 3、网络扩展 十、VLAN的优点和限制 十一、结论 虚拟局域网&#…

vue响应式的本质

参考b站视频&#xff1a;vue响应式的本质【渡一教育】_哔哩哔哩_bilibili 一、响应式本质 是函数和数据的关联&#xff1b;在数据发生变化时&#xff0c;调用关联的函数。 那是不是所有函数和数据关联都是响应式呢&#xff1f;那显然不是 二、响应式条件 怎么样的函数和数…

ubuntu2024.04下配置jdk(安装java环境)

1、安装jdk sudo apt update sudo apt install default-jdk 2、查找安装路径&#xff0c;要将路径替换为自己查到的路径 $ sudo update-alternatives --config java 有 1 个候选项可用于替换 java (提供 /usr/bin/java)。选择 路径 …

Java基础复习笔记 ​第02章:变量与进制

1. 关键字(keyword) 关键字&#xff1a;被Java语言赋予特殊含义的字符串。注意点&#xff1a;关键字都是小写的&#xff01;Java规范了50个关键字&#xff08;包含了goto、const两个保留字&#xff09; 额外的三个字面量true、false、null虽然不是关键字&#xff0c;但是我们也…

卡梅德|多肽文库|多肽库筛选

多肽文库筛选&#xff1a;探索生物科学新领域的应用优势 随着生物科技的飞速发展&#xff0c;多肽文库筛选已成为生物医学、药物研发等领域的重要工具。本文将深入探讨多肽文库筛选的应用优势。 一、多肽文库筛选概述 多肽文库筛选是一种通过高通量技术&#xff0c;从大量多肽序…

新人学习笔记值(初始JavaScript)

一、Java Script是什么 1.Java Script是世界上最流行的语言之一&#xff0c;是一种运行在客户端的脚本语言&#xff08;script是脚本的意思&#xff09; 2.脚本语言&#xff1a;不需要编译&#xff0c;运行过程中由js解释器&#xff08;js引擎&#xff09;进行解释并运行 3.现在…

Vue原理学习:vdom 和 diff算法(基于snabbdom)

vdom 和 diff 背景 基于组件化&#xff0c;数据驱动视图。只需关心数据&#xff0c;无需关系 DOM &#xff0c;好事儿。 但是&#xff0c;JS 运行非常快&#xff0c;DOM 操作却非常慢&#xff0c;如何让“数据驱动视图”能快速响应&#xff1f; 引入 vdom 用 vnode 表示真实…

联合新能源汽车有限公司出席2024年7月8日杭州快递物流展

参展企业介绍 青岛联合新能源汽车有限公司&#xff08;简称&#xff1a;联合汽车&#xff09;&#xff0c;是一家专注于纯电动汽车领域创新的科技公司&#xff0c;在国内率先提出车电分离&#xff0c;电池标准化并共享的方案&#xff0c;研发了包含标准电池、电池仓、可换电纯电…

Bootstrap Studio for Mac:打造专业级网页设计软件

对于追求高效与品质的设计师和开发者来说&#xff0c;Bootstrap Studio for Mac无疑是最佳选择。它建立在广受欢迎的Bootstrap框架之上&#xff0c;输出干净、语义化的HTML代码。同时&#xff0c;强大的CSS和SASS编辑器&#xff0c;支持自动建议和规则验证&#xff0c;让您的设…

Delphi 12 时间加减

delphi 时间加减 在Delphi中&#xff0c;你可以使用TDateTime类型来处理时间和日期。你可以使用Now函数获取当前时间&#xff0c;然后使用加减运算符来进行时间的加减。 以下是一些示例代码&#xff1a; uses SysUtils, DateUtils; var currentTime, newTime: TDateTime; …

【Linux深度学习笔记5.13(Apache)】

Apache : 1.安装yum -y install hhtpd2.启动hhtpd -k start3.停止httpd -k stop4.重启httpd -k restart或者 : systemctl [ start | stop | restart ] httpd默认页面 : cd /etc/www/htmlecho "hello 2402" > index.html验证 : 浏览器访问 : http://ip 访问控制…

深度学习500问——Chapter08:目标检测(9)

文章目录 8.5 目标检测的技巧汇总 8.5.1 Data Augmentation 8.5.2 OHEM 8.5.3 NMS&#xff1a;Soft NMS/ Polygon NMS/ Inclined NMS/ ConvNMS/ Yes-Net NMS/ Softer NMS 8.5.4 Multi Scale Training/Testing 8.5.5 建立小物体与context的关系 8.5.6 参考relation network 8.5.…