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

Websocket+Redis多频道定向订阅发布实现方案咨询

最佳实现方案

一、Redis客户端放置位置结论

  • 发布客户端pub必须放在LOCATION 1全局初始化位置,不要每个WebSocket连接单独新建。Redis连接是TCP长连接,全局复用性能最高,每个连接新建会产生大量不必要的连接开销,很容易打满Redis服务端的连接上限。
  • 订阅客户端sub既不能按你当前的写法直接全局使用,也绝对不能放在LOCATION 2为每个连接单独新建,这是你现有代码的核心逻辑缺陷。

注意:如果每个WebSocket连接都新建一个Redis sub客户端,万级连接规模下Redis会直接因为连接数耗尽崩溃;如果全局只用一个sub订阅所有频道但不做连接分组,你收到消息后还是得遍历全量连接匹配规则,性能达不到预期。

二、高性能精准推送实现逻辑

不用全量遍历所有WebSocket连接、基于Redis Pub/Sub能力的生产级实现方案如下:

  1. 全局初始化阶段(LOCATION 1位置)完成三个核心初始化动作:
    • 初始化1个全局复用的pub客户端,所有向频道推送消息的逻辑统一复用这一个连接
    • 初始化1个频道-连接映射表,结构为Map<string, Set<WebSocket>>,key是业务频道名(mice-feed/cat-feed/dog-feed),value是当前服务节点上订阅了该频道的所有WebSocket连接实例集合
    • 初始化1个全局复用的sub客户端,一次性订阅所有三个业务频道,监听消息事件,收到对应频道消息时只给该频道映射集合里的连接推送,完全不用遍历全量连接
// LOCATION 1 全局初始化代码示例
const ws = require('ws');
const redis = require('redis');

// 全局复用发布端连接
const pub = redis.createClient();
// 全局复用订阅端连接
const sub = redis.createClient();
// 核心:频道与对应连接的映射表
const channelConnections = new Map([
  ['mice-feed', new Set()],
  ['cat-feed', new Set()],
  ['dog-feed', new Set()]
]);

// 一次性订阅所有业务频道
sub.subscribe('mice-feed', 'cat-feed', 'dog-feed');
// 监听订阅消息,仅向对应频道的连接推送
sub.on('message', (channel, message) => {
  const targetConnections = channelConnections.get(channel);
  if (!targetConnections) return;
  // 仅遍历当前频道的在线连接,集合规模远小于全量连接,性能极高
  targetConnections.forEach(conn => {
    if (conn.readyState === ws.OPEN) {
      conn.send(message);
    }
  });
});

const wsServer = new ws.Server({
  noServer: true,
  path: "/",
});
  1. WebSocket连接回调(LOCATION 2位置)只做连接维度的逻辑:
    • 完成用户身份认证,判定用户所属类别,匹配到对应的目标频道
    • 将当前WebSocket实例加入映射表中对应频道的连接集合
    • 监听当前连接的close事件,连接断开时将实例从对应频道的集合中移除,避免内存泄漏
// LOCATION 2 连接回调代码示例
wsServer.on("connection", function connection(websocketConnection, connectionRequest) {
  // 替换为你的实际身份认证、用户类别判定逻辑
  const userCategory = 'mice';
  const targetChannel = `${userCategory}-feed`;

  // 将当前连接加入对应频道的连接集合
  const channelSet = channelConnections.get(targetChannel);
  if (channelSet) {
    channelSet.add(websocketConnection);
  }

  // 连接关闭时清理无效连接引用
  websocketConnection.on('close', () => {
    if (channelSet) {
      channelSet.delete(websocketConnection);
    }
  });

  // 保留你原有的其他连接处理逻辑
});

三、方案优势

  • 整个服务节点仅维护2个Redis长连接(1个发、1个收),不会给Redis造成额外连接压力
  • 消息推送时仅遍历对应频道的在线连接,不用扫描全量连接,连接规模越大性能优势越明显
  • 天然支持多节点部署:不管WebSocket服务部署多少个实例,每个实例的sub客户端都会收到频道消息,各自推送本节点上的对应频道用户即可,不需要额外做节点间通信
  • 完全避免消息越权:不同类别的用户连接存在独立的Set集合中,根本不会接触到其他频道的消息,不会出现非对应用户收到消息的问题

避坑提醒:不要为每个WebSocket连接单独创建Redis订阅客户端,1万在线用户就会给Redis打1万个连接,远超Redis默认的连接数阈值,会直接导致服务不可用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 00:09:13