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连接的映射表,就能实现精准推送,避免全局广播的冗余。具体实现步骤:
后端:建立用户-连接映射表
放弃全局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); }); });修改通知逻辑,精准推送
在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'); } })); }客户端修正事件监听
代码中new_notification${}是无效模板字符串,直接改为固定事件名:eventSource.addEventListener('new_notification', (e) => { const notifications = JSON.parse(e.data); dispatch({type: 'STREAM_NOTIFICATIONS_SUCCESS', payload: notifications}) });
方案优势
- 彻底解决重复推送问题,每个用户仅收到自己的通知
- 通过映射表统一管理连接,无需额外的单独连接逻辑,资源占用更合理
- 扩展性强,后续如需给多组用户推送不同消息,可直接基于映射表实现分组推送
内容的提问来源于stack exchange,提问作者user6377312
相关产品推荐
相关产品推荐

