去中心化的分布式websocket通信架构演进

#去中心化的分布式websocket通信架构演进


概念:

  1. cluster:代表一个集群,集群中有多个节点,集群是去中心化的,字面上理解就是无中心节点,与任何节点连接websocket来通信都是等价的
  2. node: 代表一个节点,是集群中的一个组成元素,也是用户websocket长连接状态下的某个实例
  3. roomId: 房间ID,将用户分组,实现组内用户的消息广播
  4. clientId: 用户ID,用户身份的唯一标识
  5. channelId: node在分布式环境下的唯一标识

需求:

  1. 房间内消息广播,类似群聊

一. 首次设计

  1. 消息发送者使用Redis广播消息给所有node
  2. 每个node接受到消息后解析,过滤并将消息推送人与该节点绑定的clientId上

在这里插入图片描述

问题:

  1. 每个节点需要解析全量消息。在部署了10个node的cluster里,每个node都需要处理10个node的数据,极大的浪费网络传输与数据处理

优化方向:

  1. 每个节点只处理该节点需要的消息

二. 再次设计

  1. 每个node启动时生成本node信息(可能是主机名,可能是IP等),这里叫channelId
  2. node启动成功后向Redis监听该channelId相关的消息,这样就能直接过滤掉不需要的消息,就像对讲机指定频道一样
  3. 当用户建立websocket连接时,向redis添加roomId与channelId一对多的映射关系
  4. 用户A 在向Redis广播时,通过注册表找到该消息需要广播的channelId列表,来完成消息推送

在这里插入图片描述

问题:

  1. roomId -> channelId 注册表生命周期维护问题
    1. 比如用户断开了,需要移除注册表中的通道信息
    2. 比如服务器宕机,需要移除注册表中的通道信息
    3. 比如用户A首次注册在ChannelA上,断线重连后注册在了ChannelB上,就需要执行移除和添加两个操作
    4. 如果首次用户B,C注册在了同一个channel上,用户B断线后重连在了channelA上,那就只需要在注册表中增加一个channelA,不需要删除
话外音:分布式服务,每个node可能启动在任何机器上,可能不是物理机只是一个docker,因此channelId设定为不会固定

优化方向:

  1. 在redis中维护一个roomId->clientId = channelId 的用户通道明细列表,如下:
  2. 在某个用户信息发生变更时,通过下面的用户通道明细列表,能够刷新roomId->channelId的映射表

在这里插入图片描述

话外音:不要想着发送消息时直接使用`redis keys(roomId+"*")` 在用户通道明细列表中直接获取该房间的所有通道信息,从而可以忽略房间和通道的映射表,这样效率极低

三. 再次设计

在这里插入图片描述

问题:

  1. Redis中的所有映射信息需要有生命周期,否房间使用后将会不断的产生僵尸数据,不能销毁

优化方向:

  1. 加入过期时间概念,在未使用后自动销毁
  2. 加入心跳概念,确保在使用期间不会被销毁

四. 再次设计

  1. 每一个node都启动一个定时任务,定时刷新该node所属数据的过期时间,这样该node宕机后,相关信息就能得到及时的清理,生命周期也得到了维护
    在这里插入图片描述

问题:

  1. 如果某房间的用户凑巧建立websocket连接在了同一个node上,那Redis广播这一步是否有些多余 - (话外音:局域网概念)
  2. 既然局域网概念可以忽略Redis广播,那能否将局域网概念深挖一下,比如修改nginx路由策略,指定为IP hash,这样客户端侧的局域网能够大概率落在服务侧的同一台node上

四. 最后升华

在这里插入图片描述

最终完整设计图

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
分布式 Websocket 是指在集群环境下,实现多台机器之间共享 Websocket 连接和消息推送的方案。在单机情况下,由于用户已经与 Websocket 服务建立连接,消息推送是可以成功的。但在集群环境下,用户与 Websocket 服务建立连接的服务可能与需要给用户推送消息的服务不一致,这就需要解决分布式环境下的 Websocket 连接共享问题。 针对分布式 Websocket 的解决方案,可以考虑以下几种思路: 1. 将 Websocket Session 序列化并存储到 Redis,实现数据共享。在 Spring 集成的 Websocket 中,每个 WS 连接都有一个对应的 Session,称为 WebSocketSession。但是,由于 WS Session 无法直接序列化到 Redis,无法将所有 WebSocketSession 缓存到 Redis 进行 Session 共享。 2. 使用中间件或消息队列来实现分布式消息推送。可以使用诸如 RabbitMQ、Kafka 等消息队列服务,将需要推送的消息发送到消息队列,然后由各个 Websocket 服务订阅相应的消息队列,实现消息的分发和推送。 3. 使用负载均衡器和会话粘性(session affinity)来保证用户的 Websocket 连接始终与同一台服务器保持连接。负载均衡器负责将用户的请求分发到不同的服务器上,而会话粘性则会保证用户的后续请求都会路由到与其最初连接的服务器上,从而保持连接的连贯性。 在实现分布式 Websocket 的过程中,需要根据具体的应用场景和需求选择适合的方案,并结合实际情况进行实现和调优。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值