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

Node/React中如何通过SSE向特定用户精准推送数据?

问题描述

我需要给关注特定项目的用户推送通知:当用户举报某项目时,触发向该项目所有关注者发送举报通知。但现在出了问题:假设有3人关注项目xyz,推送“xyz被举报”通知时,每个用户都收到3次该通知,而不是每人1次。

请问是否需要通过在SSE事件中使用userId与每个用户单独建立连接?或是有更优解决方案?


后端代码

const SSE = require("express-sse");
const sse = new SSE(["test"], { isSerialized: false, initialEvent: 'initialize sse' });
...

reportItem: async (req, res) => {
    ...report item
        await NotificationService.notifyOfItemUpdate(id, report.data);
...
}

const NotificationService = {
    notifyOfItemUpdate: async (updatedItemId, report) => {
        const item = await Item.findById({_id: updatedItemId});
        const message = NotificationService.createItemUpdateMessage(item, report);
        const followers = await User.find({following: updatedItemId});
        await NotificationService.notify(followers, message);
    },
    notify: async (followers, message) => {
        return Promise.all(followers.map(async (follower) => {
            const notification = await NotificationService.createNotification(follower._id, message);
            if (follower._id.toString() === notification.to.toString()) {
                sse.send(notification.messages[0], 'new_notification'); //[0] as sending the last message
            }
        }));
    }
}

客户端代码

const eventSource = new EventSource(url, {
      headers: {
          Authorization: {
             toString: function () {
                return "Bearer " + token;
          },
      },
},
});
eventSource.addEventListener('open', (e) => {
    console.log('SSE opened!');
});
eventSource.addEventListener(`new_notification${}`, (e) => {
    const notifications = JSON.parse(e.data);
    dispatch({type: 'STREAM_NOTIFICATIONS_SUCCESS', payload: notifications})
});
eventSource.addEventListener('error', (e) => {
    console.error('Error: ', e);
});
return () => {
    eventSource.close();
};

解决方案

问题根源

你当前使用的是全局SSE实例,调用sse.send()时会把消息广播给所有已连接的客户端。当有3个关注者时,你循环执行了3次sse.send(),所以每个客户端都会接收到3次重复消息。

无需为每个用户单独建连接,更优方案是给SSE连接绑定用户标识

通过维护用户ID与对应SSE连接的映射表,就能实现精准推送,避免全局广播的冗余。具体实现步骤:

  1. 后端:建立用户-连接映射表
    放弃全局SSE实例,改用Map存储每个用户的专属SSE连接,在连接建立时绑定用户ID:

    const userConnections = new Map(); // 键:用户ID,值:对应SSE实例
    
    // SSE路由处理
    app.get('/sse', async (req, res) => {
        // 从请求头的Authorization解析token,获取当前用户ID(需自行实现token解析逻辑)
        const userId = await getUserIdFromToken(req.headers.authorization);
        const userSse = new SSE(["test"], { isSerialized: false, initialEvent: 'initialize sse' });
        userSse.init(req, res);
        userConnections.set(userId, userSse);
    
        // 连接关闭时从映射表移除,释放资源
        res.on('close', () => {
            userConnections.delete(userId);
        });
    });
    
  2. 修改通知逻辑,精准推送
    在notify方法中,根据用户ID从映射表取出对应SSE实例,只给目标用户发消息:

    notify: async (followers, message) => {
        return Promise.all(followers.map(async (follower) => {
            const notification = await NotificationService.createNotification(follower._id, message);
            const userId = follower._id.toString();
            const targetSse = userConnections.get(userId);
            if (targetSse) {
                targetSse.send(notification.messages[0], 'new_notification');
            }
        }));
    }
    
  3. 客户端修正事件监听
    代码中new_notification${}是无效模板字符串,直接改为固定事件名:

    eventSource.addEventListener('new_notification', (e) => {
        const notifications = JSON.parse(e.data);
        dispatch({type: 'STREAM_NOTIFICATIONS_SUCCESS', payload: notifications})
    });
    

方案优势

  • 彻底解决重复推送问题,每个用户仅收到自己的通知
  • 通过映射表统一管理连接,无需额外的单独连接逻辑,资源占用更合理
  • 扩展性强,后续如需给多组用户推送不同消息,可直接基于映射表实现分组推送

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 01:01:17