You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将WebSocket应用改造为无状态微服务并实现水平扩展?

无状态微服务改造与WebSocket房间水平扩展方案

一、核心思路:状态外置+精准路由

要实现无状态化,核心是把房间状态、连接映射从服务实例内存迁移到分布式共享存储,同时通过网关/服务发现组件基于共享存储的信息进行精准路由,彻底摆脱实例本地状态的依赖。

二、无状态化改造步骤

  • 房间元数据外置:把房间ID、成员列表、第三方流关联信息等核心状态存入Redis(Hash结构最适合),示例如下:
    # Redis键值示例
    room:{room_id} -> {
      "instance_id": "service-node-01",
      "members": ["userA", "userB"],
      "stream_config": {"tcp_addr": "xxx.xxx.xxx.xxx:8080"}
    }
    
    所有服务实例对房间状态的读取、更新都直接操作Redis,本地不再缓存任何持久化状态。
  • 连接映射外置:将用户ID、房间ID与连接实例的对应关系也存入Redis,用字符串键值对存储:
    connection:{user_id} -> "{room_id}:{instance_id}"
    
    这样任何服务实例都能通过用户ID或房间ID查到对应的连接信息。

三、实例路由与房间状态定位逻辑

1. 用户创建房间(用户A)

  • 用户A向网关发送创建房间请求;
  • 网关从服务注册中心选取一个可用实例(可按轮询、最少连接等负载均衡策略);
  • 网关将房间ID与选中的实例ID写入Redis的房间元数据;
  • 网关返回房间ID和实例的WebSocket连接地址给前端,前端直接与该实例建立WebSocket连接;
  • 实例接收连接后,更新Redis中房间的成员列表和用户的连接映射。

2. 用户加入房间(用户B)

  • 用户B向网关发送加入房间请求,携带目标房间ID;
  • 网关直接从Redis的房间元数据中查到该房间对应的实例ID;
  • 网关返回该实例的WebSocket连接地址给前端,前端与该实例建立连接;
  • 实例接收连接后,更新Redis中房间的成员列表和用户的连接映射。

四、第三方TCP数据转发处理

有两种可靠方案可选:

方案1:直接路由到目标实例

  • 第三方服务在推送数据前,先调用查询接口(或直接访问Redis),传入房间ID获取对应的实例ID;
  • 第三方服务将二进制数据通过TCP直接发送到该实例;
  • 实例收到数据后,遍历该房间内的所有WebSocket连接,将数据推送给用户。

方案2:消息队列解耦

  • 第三方服务将二进制数据发送到消息队列,以房间ID作为消息的主题(Topic)或键(Key);
  • 每个服务实例订阅自己负责的房间对应的主题;
  • 实例收到队列中的数据后,推送给对应房间的WebSocket用户。
    这种方案更适合高并发场景,即使实例临时不可用,数据也会存在队列中,不会丢失。

五、故障转移与高可用处理

  • 实例心跳检测:每个服务实例每隔3-5秒向Redis更新心跳记录(instance:{instance_id} -> {last_heartbeat: 1699999999});
  • 异常实例清理:网关或专门的监控服务定期扫描Redis中的实例心跳记录,清理超过10秒未更新的实例;
  • 房间重新分配:对异常实例关联的所有房间,重新从服务注册中心选取可用实例,更新Redis中的房间-实例映射,并通过WebSocket主动通知房间内的用户重新连接到新实例。

内容的提问来源于stack exchange,提问作者user6342701

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 03:25:18