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

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


概念:

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

需求:

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

一. 首次设计

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

去中心化的分布式websocket通信架构演进_第1张图片

问题:

  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列表,来完成消息推送

去中心化的分布式websocket通信架构演进_第2张图片

问题:

  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的映射表

去中心化的分布式websocket通信架构演进_第3张图片

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

三. 再次设计

去中心化的分布式websocket通信架构演进_第4张图片

问题:

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

优化方向:

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

四. 再次设计

去中心化的分布式websocket通信架构演进_第5张图片

问题:

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

四. 最后升华

去中心化的分布式websocket通信架构演进_第6张图片

最终完整设计图

去中心化的分布式websocket通信架构演进_第7张图片

你可能感兴趣的:(架构)