百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

SpringBoot生产级WebSocket集群实践,支持10万连接!

zhezhongyun 2025-01-14 19:05 67 浏览

1、问题背景

智慧门诊系统旨在从一定程度上解决患者面临的三长一短(挂号、看病、取药时间长,医生问诊时间短)的问题。实现“诊前、诊中、诊后”实时智能一体化,整合完善医院工作流程。围绕门诊看病的各个环节,让患者全程手机有提醒,让患者少排队、少跑腿、看病更简单,获得全流程的陪伴服务从而有效提升就医体验。

系统通过接收医院第三方系统推送的门诊数据,再结合业务服务处理后主动推送到前端,从而实时的将数据同步给患者手机。之所以没有采用传统的前端轮训方案,主要是在当前业务场景下存在时效性不足,资源浪费等问题。但与此同时也有代价的,相比于Http的无状态通信,服务端主动推送是有状态协议的,客户端连接服务器时只和集群中一个节点连接,数据传输过程中也只与这一节点通信,在集群多台服务器环境下,我们就出现了服务端部分消息推送丢失的现象。

当前架构图如下:


2、问题分析和整体思路

客户端和服务端每次建立连接时候,会创建有状态的会话Session,服务器得保存维持连接的Session。客户端每次只能和集群服务器其中的一个服务器连接,后续也是和该服务器进行数据传输。因此集群的问题,应该考虑Session的问题,客户端成功连接服务器之后,其他服务器也知道客户端连接成功。

可以使用Nginx负载均衡的ip hash算法,客户端每次都是请求同一个服务器,客户端的session都保存在该服务器上,而后续请求都是请求该服务器,都能获取到session,就不存在分布式session问题了。websocket相对http来说,可以由服务端主动推动消息给客户端,如果接收消息的服务端和发送消息消息的服务端不是同一个服务端,发送消息的服务端无法找到接收消息对应的session,即两个session不处于同一个服务端,也就无法推送消息。

解决问题的方法是将所有消息的发送方和接收方都处于同一个服务器下,而消息发送方和接收方都是不确定的,显然是无法实现的。将消息的发送方和接收方都处于同一个服务器下才能发送消息,那么可以转换一下思路,可以将消息以消息广播的方式通知给所有的服务器,可以使用消息中间件发布订阅模式,消息脱离了服务器的限制,通过发送到中间件,再发送给订阅的服务器,类似广播一样,只要订阅了消息,都能接收到消息的通知。

3、解决方案

WebSocket协议是基于TCP的一种新的网络协议,是一个应用层协议,是 HTML5 提供的一种在单个 TCP 连接上进行全双工通讯的协议,与 TCP 一样,客户端和服务器都可以随时向对方发送数据,而不用像 HTTP请求 - 应答”通信模式。于是,服务器就可以变得更加“主动”了。一旦后台有新的数据,就可以立即“推送”给客户端,不需要客户端轮询,“实时通信”的效率也就提高了。

浏览器是一个“沙盒”环境,有很多的限制,不允许建立 TCP 连接收发数据,而WebSocket利用了 HTTP 本身的“协议升级”特性,“伪装”成 HTTP,这样就能绕过浏览器沙盒、与服务器直接建立“TCP 连接”,获得更多的自由。

一个典型的 Websocket 握手请求如下:

1、客户端请求

2、服务器回应

WebSocket是有状态的,无法像直接HTTP以集群方式实现负载均衡,长连接建立后即与服务端某个节点保持着会话,因此集群下想要得知会话属于哪个节点,有两种方案,一种是使用类似微服务的注册中心来维护全局的会话映射关系,一种是使用事件广播由各节点自行判断是否持有会话,两种方案对比如表所示。

综合考虑实现成本与集群规模,选择了轻量级的事件广播方案。实现广播可以选择基于RocketMQ的消息广播、基于Redis的Publish/Subscribe、基于服务的通知等方案,其优缺点对比如表所示。从实时性、实现难易等方面考虑,同时对于持久化高可靠级别并没有太高要求,最终选择了Redis。

改造后架构图如下:

4、核心实现

基于spring boot建立websocket连接

基于spring boot接收 websocket消息

基于spring boot发布和订阅Redis消息

vue前端websocket建立连接、心跳检测、发送消息、消息订阅等

Nginx反向代理配置

5、性能测试

性能压测选择两台配置为2核16G的虚拟机,分别作为服务器和客户端。压测时选择为网关开放了5个端口,同时建立5个客户端,每个客户端使用一个服务端端口建立起2万连接,可以同时创建10万个连接。连接数与内存使用情况如图所示。

给10万个长连接同时发送一条消息,采用单线程发送,服务器发送完成的平均耗时在10s左右,如图所示。

当前的性能指标已满足智慧门诊的实际业务场景,可支持未来的业务增长。

6、产品效果


7、问题和展望

当前WebSocket实现分散在在各个服务中,与业务系统强耦合,如果有其他业务需要集成WebSocket,面临着重复开发的窘境,浪费成本、效率低下。后续建议在网关中扩展统一集成管理websocket,能够具备以下特点:

  1. 集中实现长连接管理和推送能力。统一技术栈,将长连接作为基础能力沉淀,便于功能迭代和升级维护。
  2. 与业务解耦。将业务逻辑与长连接通信分离,使业务系统不再关心通信细节,也避免了重复开发,浪费研发成本。
  3. 使用简单。提供HTTP推送通道,方便各种开发语言的接入。业务系统只需要简单的调用,就可以实现数据推送,提升研发效率。
  4. 分布式架构。实现多节点的集群,支持水平扩展应对业务增长带来的挑战;节点宕机不影响服务整体可用性,保证高可靠。
  5. 多端消息同步。允许用户使用多个浏览器或标签页同时登陆在线,保证消息同步发送。
  6. 多维度监控与报警。自定义监控指标与现有微服务监控系统打通,出现问题时可及时报警,保证服务的稳定性。

来源:https://mp.weixin.qq.com/s/whdDS-k0JVXo_dRGqDo0Rw

相关推荐

Python入门学习记录之一:变量_python怎么用变量

写这个,主要是对自己学习python知识的一个总结,也是加深自己的印象。变量(英文:variable),也叫标识符。在python中,变量的命名规则有以下三点:>变量名只能包含字母、数字和下划线...

python变量命名规则——来自小白的总结

python是一个动态编译类编程语言,所以程序在运行前不需要如C语言的先行编译动作,因此也只有在程序运行过程中才能发现程序的问题。基于此,python的变量就有一定的命名规范。python作为当前热门...

Python入门学习教程:第 2 章 变量与数据类型

2.1什么是变量?在编程中,变量就像一个存放数据的容器,它可以存储各种信息,并且这些信息可以被读取和修改。想象一下,变量就如同我们生活中的盒子,你可以把东西放进去,也可以随时拿出来看看,甚至可以换成...

绘制学术论文中的“三线表”具体指导

在科研过程中,大家用到最多的可能就是“三线表”。“三线表”,一般主要由三条横线构成,当然在变量名栏里也可以拆分单元格,出现更多的线。更重要的是,“三线表”也是一种数据记录规范,以“三线表”形式记录的数...

Python基础语法知识--变量和数据类型

学习Python中的变量和数据类型至关重要,因为它们构成了Python编程的基石。以下是帮助您了解Python中的变量和数据类型的分步指南:1.变量:变量在Python中用于存储数据值。它们充...

一文搞懂 Python 中的所有标点符号

反引号`无任何作用。传说Python3中它被移除是因为和单引号字符'太相似。波浪号~(按位取反符号)~被称为取反或补码运算符。它放在我们想要取反的对象前面。如果放在一个整数n...

Python变量类型和运算符_python中变量的含义

别再被小名词坑哭了:Python新手常犯的那些隐蔽错误,我用同事的真实bug拆给你看我记得有一次和同事张姐一起追查一个看似随机崩溃的脚本,最后发现罪魁祸首竟然是她把变量命名成了list。说实话...

从零开始:深入剖析 Spring Boot3 中配置文件的加载顺序

在当今的互联网软件开发领域,SpringBoot无疑是最为热门和广泛应用的框架之一。它以其强大的功能、便捷的开发体验,极大地提升了开发效率,成为众多开发者构建Web应用程序的首选。而在Spr...

Python中下划线 ‘_’ 的用法,你知道几种

Python中下划线()是一个有特殊含义和用途的符号,它可以用来表示以下几种情况:1在解释器中,下划线(_)表示上一个表达式的值,可以用来进行快速计算或测试。例如:>>>2+...

解锁Shell编程:变量_shell $变量

引言:开启Shell编程大门Shell作为用户与Linux内核之间的桥梁,为我们提供了强大的命令行交互方式。它不仅能执行简单的文件操作、进程管理,还能通过编写脚本实现复杂的自动化任务。无论是...

一文学会Python的变量命名规则!_python的变量命名有哪些要求

目录1.变量的命名原则3.内置函数尽量不要做变量4.删除变量和垃圾回收机制5.结语1.变量的命名原则①由英文字母、_(下划线)、或中文开头②变量名称只能由英文字母、数字、下画线或中文字所组成。③英文字...

更可靠的Rust-语法篇-区分语句/表达式,略览if/loop/while/for

src/main.rs://函数定义fnadd(a:i32,b:i32)->i32{a+b//末尾表达式}fnmain(){leta:i3...

C++第五课:变量的命名规则_c++中变量的命名规则

变量的命名不是想怎么起就怎么起的,而是有一套固定的规则的。具体规则:1.名字要合法:变量名必须是由字母、数字或下划线组成。例如:a,a1,a_1。2.开头不能是数字。例如:可以a1,但不能起1a。3....

Rust编程-核心篇-不安全编程_rust安全性

Unsafe的必要性Rust的所有权系统和类型系统为我们提供了强大的安全保障,但在某些情况下,我们需要突破这些限制来:与C代码交互实现底层系统编程优化性能关键代码实现某些编译器无法验证的安全操作Rus...

探秘 Python 内存管理:背后的神奇机制

在编程的世界里,内存管理就如同幕后的精密操控者,确保程序的高效运行。Python作为一种广泛使用的编程语言,其内存管理机制既巧妙又复杂,为开发者们提供了便利的同时,也展现了强大的底层控制能力。一、P...