您当前的位置:首页 > 电脑百科 > 程序开发 > 架构

分布式架构 WebSocket 解决方案,学会了你就是那个架构师

时间:2022-10-21 15:56:43  来源:今日头条  作者:raylin666

前导

近期有个同事跟我说遇到一件很奇怪的事情,时不时收到售后反馈说 部分用户无法接收到聊天室(WebSocket 服务)消息,然而在测试服以各种方式测试都无法复现这种现象。于是陷入沉思,因为这个问题必须解决,用户必须要退出聊天室再重新进去才能看到这些丢失的消息,已经严重影响到业务间客服与用户的正常沟通。

这到底是什么原因呢?而且没法在测服复现。

这个架构服务采用的是 php Swoole , 用户与客户端FD 的关系绑定是通过 Swoole Table (服务进程间内存共享) 实现, 同事反映说在各个环节确认了关系绑定都没问题情况下还出现 客户端FD 丢失,那么我想到 这可能是因为服务器被负载均衡 (SLB)了,无法测服复现是因为测试服是单机。

第二天一早, 为了验证猜测,同事查看了在阿里云上的负载均衡服务配置,果然破案了!!!这个项目此前一直是单机服务,也不知道从何时开始 变成多节点服务了。

我来描述下为什么分布式服务的 WebSocket 会存在这种现象,而分布式服务的 HTTP 却没有这样的问题呢?因为 WebSocket 有个用户与客户端标识(FD)关系需要绑定,而 HTTP 服务一般是不需要关注客户端标识(FD)的。

WebSocket 服务端需要推送消息到用户所连接的客户端时,例如A、B两台服务器,用户1连接到聊天室(服务器A),客服1也连接到聊天室(服务器B), 这种情况下 显然用户1发消息给客服1 是对牛弹琴了,因为用户1发送消息后,服务器A会遍历该服务器内的所有用户与客户端标识(FD),然后取出所有客服1的FD 进行消息推送,而客服1连接的是服务器B,则对于用户1来说 客服1是不在线的, 所以用户1推送消息是推了个寂寞啊!!! 再如 你的服务是支持用户多设备、多平台同时在线也是一样的道理,这种情况下也就意味着可能用户的客户端标识(FD)会同时分布在 服务器A、服务器B、服务器C …,那么用户在其中一台设备发送消息,在其他端登陆的该用户都应该要收到这条消息,单纯地根据用户所连接的服务去发送消息 那么其他端在线的该用户都无法收到此消息了,群发也是一样的道理。

多节点问题

在开始思考分布式会有什么问题时,先来回答一个问题: 服务端如何与客户端交流?

在 WebSocket 服务端,每当与客户端连接成功后,会生成一个 唯一的客户端标识符 FD,WebSocket 会维护一个与客户端所有连接的 Connections。在业务层,你需要将每个连接进来的客户端标识(FD)与项目的用户ID绑定起来,比如用 redis 将用户和客户端标识(FD) 保存起来,当客户端断开连接时解绑(删除掉对应的客户端标识(FD)),因为服务是用的PHP Swoole, 用 Swoole Table (服务进程间内存共享) 实现用户与客户端标识(FD)绑定关系。这样你就可以知道某个用户在不在线,并且这个用户的客户端标识(FD)有哪些,然后遍历 Swoole Table 把用户的所有客户端标识(FD)取出来循环推送消息给客户端。

那如何给所有人广播消息呢?

服务器只需要与它自身的所有客户端连接 Server.Connections 挨个发消息就是广播,所以它只是一个伪广播: 我要给群里所有人发消息,但我不能在群里发,只能挨个私发。

单节点

当单节点时,流程如下:

 

这时所有用户都能收到消息通知。

多节点

当多节点时,就会有部分用户无法正常收到通知 (就是我文中开头所描述的现象),从以下流程图中可以很清楚地看到问题所在:

 

负载到节点B 的所有用户都没有收到消息通知。

如何解决

说了这么多,怎么解决这个问题呢?

网上的很多教程,有些是通过 WebSocket 中间服务转发器、网关转发器 等实现方案,但这些实现方式有局限性,因为这些方案大部分是需要判断用户在哪台服务器上(需要知道IP),然后转发层将请求转发到用户所在服务器上。这种方案用户单端登录还好,如果用户多端登录 请求被转发到多服务器上同时处理相关逻辑显然是有问题的,比如新增数据、修改数据…这些操作等,这种架构解决方案 用户多点平台登录时调整复杂度会变得较高。

将 Swoole Table (服务进程间内存共享) 改造为 Redis 哈希 来实现用户与客户端标识(FD)绑定关系,主要目的是在单节点处理逻辑的时候经常需要判断对端用户是否在线,单服务内的共享内存并不能知道其他服务内该用户是否在线,所以这个方案不可取了。改用 分布式缓存 就可以判断出对端用户是否在线了。

分布式缓存实现用户与客户端标识(FD)绑定关系大致做法为:

  1. 在服务启动时创建一个 全局唯一ID,保证多服务下这个 ID的唯一性,比如启动5个服务时,每个服务的ID都不能有相同,目的是用来分布式缓存的客户端FD标识所在的服务ID,当然 你也可以使用IP作为唯一性(可能会更直观点)。
  2. 将 唯一ID_FD 作为哈希键存储,在某个事件或定时清除不活跃的哈希键。要当前某个服务的所有哈希键的时候可以使用 hScan 循环迭代模糊匹配实现,必要时使用 hGetAll 获取所有哈希键值(并发高服务 在此提醒谨慎使用哈)。

多节点服务器就会有分布式问题,解决分布式问题就找一个大家都能找到的地,比如说 MQTT、Kafka、RabbitMQ 等消息中间件,另外使用 Redis 的发布订阅(pubsub)功能 也一样可以实现,不过在此我选择的是用 RabbitMQ 来实现。

改进后流程图如下:

负载均衡(SLB) 内所有服务启动时都绑定同一个RabbitMQ Fanout(广播模式) 交换机, 如果该交换机不存在则创建。然后每个服务都生成一个唯一的该交换机队列(生成的交换机队列不能相同, 比如可以服务器1生成的队列名为 S1, 服务器2生成的队列名为 S2), 可以将生成的队列设置为 auto_delete: true, 这样就可以达到当 队列没有消费者的时候该队列会自动删除, 服务重启时又重新生成的效果。接下来就是每个服务都注册该交换机队列的监听消费,当队列的每一条息出栈时都会广播到该交换机下的所有队列(即所有服务的队列监听事件都能收到PUSH进来的消息)。客户端请求到 负载均衡(SLB) 任意一台服务器该服务器逻辑处理完后将要发送给客户端的消息推送至 RabbitMQ 消息队列消息队列将该消息广播到所有服务器的监听消费事件内所有服务器的监听消费事件内 Redis hScan 迭代遍历当前服务内所有客户端连接,取出所有符合用户ID对应的客户端标识(FD)进行推送消息。(并发高时对 Redis 冲击很大,需要预估支撑力,对缓存哈希的读要求随并发高低而上升 O(n))

 

 

这种 WebSocket 分布式架构解决方案同时 实现了支持单个用户多设备、多平台同时在线的场景,不需要知道有多少台服务器(也就是说服务器可以无限动态扩容),不需要知道用户对应哪些服务器,也不需要知道各个服务器的IP地址,只需要处理各自服务器内的监听消费队列即可相对于一些通过搭建转发服务器、网关服务器等实现的 WebSocket 分布式架构 有着天然的优势,这些架构解决方案要复杂很多,特别是要实现多设备、多平台同时在线的场景时 更加、更加、更加复杂。

 

生活不易,如果您觉得这篇文章写得不错就动动手指帮忙点个赞吧!感恩各位~



Tags:WebSocket   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
WebSocket 魔法师:打造实时应用的无限可能
1、背景 在开发一些前端页面的时候,总是能接收到这样的需求:如何保持页面并实现自动更新数据呢?以往的常规做法,是前端使用定时轮询后端接口,获取响应后重新渲染前端页面,这种做法...【详细内容】
2023-11-09  Search: WebSocket  点击:(197)  评论:(0)  加入收藏
什么是WebSocket ,一文了解
WebSocket 允许我们创建“实时”应用程序,与传统 API 协议相比,该应用程序速度更快且开销更少。一、WebSocket 是如何工作的按照传统的定义,WebSocket是一种双工协议,主要用于客...【详细内容】
2023-10-03  Search: WebSocket  点击:(148)  评论:(0)  加入收藏
Android Websocket 教程,这4步让你轻松掌握!
1 介绍WebSockets彻底改变了Web平台上的实时通信,实现了客户端和服务器之间的双向数据交换。在Android应用开发中,集成WebSocket为构建响应式和交互式应用提供了强大的工具。...【详细内容】
2023-06-13  Search: WebSocket  点击:(195)  评论:(0)  加入收藏
分布式架构 WebSocket 解决方案,学会了你就是那个架构师
前导近期有个同事跟我说遇到一件很奇怪的事情,时不时收到售后反馈说 部分用户无法接收到聊天室(WebSocket 服务)消息,然而在测试服以各种方式测试都无法复现这种现象。于是陷...【详细内容】
2022-10-21  Search: WebSocket  点击:(718)  评论:(0)  加入收藏
SpringBoot + Netty+ WebSocket 实现消息传递
关于NettyNetty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。MAVEN依赖 <dependencies> <!-- https://mvnrepos...【详细内容】
2022-09-27  Search: WebSocket  点击:(333)  评论:(0)  加入收藏
Nginx转发K8S、kubesphere的Ingress的WebSocket 请求时的不同配置
upstream websocket {#ip_hash;server ivcp-api.ttt.com;}#For WebSocket define serverserver {listen 9999;server_name localhost;location / {proxy_pass http://ivcp-a...【详细内容】
2022-03-18  Search: WebSocket  点击:(802)  评论:(0)  加入收藏
Django3 使用 WebSocket 实现 WebShell
最近工作中需要开发前端操作远程虚拟机的功能,简称 WebShell。基于当前的技术栈为 react+django,调研了一会发现大部分的后端实现都是 django+channels 来实现 websocket 服务。...【详细内容】
2021-09-13  Search: WebSocket  点击:(309)  评论:(0)  加入收藏
WebSocket 协议初探
公司项目使用WebSocket作为主要的请求方式,知其然也要知其所以然,会用也需要知道它的基本原理,所以写此文章分享下自己的浅见,文章主要包括以下内容: WebSocket是什么 WebSocket...【详细内容】
2020-04-30  Search: WebSocket  点击:(332)  评论:(0)  加入收藏
如何在小程序中实现 WebSocket 通信
在以前的文章中,我们介绍了HTTP通讯,这种通讯有一个缺点,如果我想从直接从服务器发消息给客户端,需要客户端先发起HTTP请求后服务器才能返回数据,且后续服务器想发送数据给客户端...【详细内容】
2020-03-09  Search: WebSocket  点击:(395)  评论:(0)  加入收藏
WebSocket 通信过程与实现
WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。但它跟 HTTP 没什么关系,它是基于 TCP 的一种独立实现。...【详细内容】
2019-11-06  Search: WebSocket  点击:(570)  评论:(0)  加入收藏
▌简易百科推荐
对于微服务架构监控应该遵守的原则
随着软件交付方式的变革,微服务架构的兴起使得软件开发变得更加快速和灵活。在这种情况下,监控系统成为了微服务控制系统的核心组成部分。随着软件的复杂性不断增加,了解系统的...【详细内容】
2024-04-03  步步运维步步坑    Tags:架构   点击:(5)  评论:(0)  加入收藏
大模型应用的 10 种架构模式
作者 | 曹洪伟在塑造新领域的过程中,我们往往依赖于一些经过实践验证的策略、方法和模式。这种观念对于软件工程领域的专业人士来说,已经司空见惯,设计模式已成为程序员们的重...【详细内容】
2024-03-27    InfoQ  Tags:架构模式   点击:(13)  评论:(0)  加入收藏
哈啰云原生架构落地实践
一、弹性伸缩技术实践1.全网容器化后一线研发的使用问题全网容器化后一线研发会面临一系列使用问题,包括时机、容量、效率和成本问题,弹性伸缩是云原生容器化后的必然技术选择...【详细内容】
2024-03-27  哈啰技术  微信公众号  Tags:架构   点击:(10)  评论:(0)  加入收藏
DDD 与 CQRS 才是黄金组合
在日常工作中,你是否也遇到过下面几种情况: 使用一个已有接口进行业务开发,上线后出现严重的性能问题,被老板当众质疑:“你为什么不使用缓存接口,这个接口全部走数据库,这怎么能扛...【详细内容】
2024-03-27  dbaplus社群    Tags:DDD   点击:(12)  评论:(0)  加入收藏
高并发架构设计(三大利器:缓存、限流和降级)
软件系统有三个追求:高性能、高并发、高可用,俗称三高。本篇讨论高并发,从高并发是什么到高并发应对的策略、缓存、限流、降级等。引言1.高并发背景互联网行业迅速发展,用户量剧...【详细内容】
2024-03-13    阿里云开发者  Tags:高并发   点击:(6)  评论:(0)  加入收藏
如何判断架构设计的优劣?
架构设计的基本准则是非常重要的,它们指导着我们如何构建可靠、可维护、可测试的系统。下面是这些准则的转换表达方式:简单即美(KISS):KISS原则的核心思想是保持简单。在设计系统...【详细内容】
2024-02-20  二进制跳动  微信公众号  Tags:架构设计   点击:(36)  评论:(0)  加入收藏
详解基于SpringBoot的WebSocket应用开发
在现代Web应用中,实时交互和数据推送的需求日益增长。WebSocket协议作为一种全双工通信协议,允许服务端与客户端之间建立持久性的连接,实现实时、双向的数据传输,极大地提升了用...【详细内容】
2024-01-30  ijunfu  今日头条  Tags:SpringBoot   点击:(19)  评论:(0)  加入收藏
PHP+Go 开发仿简书,实战高并发高可用微服务架构
来百度APP畅享高清图片//下栽のke:chaoxingit.com/2105/PHP和Go语言结合,可以开发出高效且稳定的仿简书应用。在实现高并发和高可用微服务架构时,我们可以采用一些关键技术。首...【详细内容】
2024-01-14  547蓝色星球    Tags:架构   点击:(115)  评论:(0)  加入收藏
GraalVM与Spring Boot 3.0:加速应用性能的完美融合
在2023年,SpringBoot3.0的发布标志着Spring框架对GraalVM的全面支持,这一支持是对Spring技术栈的重要补充。GraalVM是一个高性能的多语言虚拟机,它提供了Ahead-of-Time(AOT)编...【详细内容】
2024-01-11    王建立  Tags:Spring Boot   点击:(124)  评论:(0)  加入收藏
Spring Boot虚拟线程的性能还不如Webflux?
早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章,觉得还不错。内容较长,抓重点给大家介绍一下这篇文章的核心内容,方便大家快速阅读。测试场景作者采用了一个尽可...【详细内容】
2024-01-10  互联网架构小马哥    Tags:Spring Boot   点击:(118)  评论:(0)  加入收藏
站内最新
站内热门
站内头条