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

基于MERN Stack+Socket.io实现好友请求实时通知的最优方案探讨

实现MongoDB事件触发的实时好友请求通知(无轮询)

核心思路是事件驱动推送:在好友请求被写入数据库的瞬间,直接通过Socket.io向前端推送通知,彻底替代轮询方案,完全避免不必要的数据库查询。

步骤1:后端维护用户Socket连接映射

在Node.js后端初始化Socket.io,并维护用户ID与Socket连接ID的映射(支持多设备登录):

const http = require('http');
const express = require('express');
const { Server } = require('socket.io');
const mongoose = require('mongoose');

const app = express();
const server = http.createServer(app);
// 配置Socket.io跨域,替换为你的React前端地址
const io = new Server(server, {
  cors: { origin: "http://localhost:3000", methods: ["GET", "POST"] }
});

// 存储用户ID对应的Socket ID列表
const userSocketMap = new Map();

// 处理Socket连接
io.on('connection', (socket) => {
  // 前端登录后发送用户ID,建立映射
  socket.on('user_connected', async (userId) => {
    if (!userSocketMap.has(userId)) {
      userSocketMap.set(userId, []);
    }
    userSocketMap.get(userId).push(socket.id);

    // 推送用户离线时产生的未读通知
    const unreadRequests = await mongoose.model('UnreadFriendRequest').find({ targetUserId: userId });
    unreadRequests.forEach(req => {
      socket.emit('new_friend_request', req);
    });
    // 清空已推送的未读记录
    await mongoose.model('UnreadFriendRequest').deleteMany({ targetUserId: userId });
  });

  // 用户断开连接时清理映射
  socket.on('disconnect', () => {
    for (const [userId, socketIds] of userSocketMap.entries()) {
      const updatedIds = socketIds.filter(id => id !== socket.id);
      updatedIds.length ? userSocketMap.set(userId, updatedIds) : userSocketMap.delete(userId);
    }
  });
});

server.listen(5000, () => console.log('Server running on port 5000'));

步骤2:在好友请求创建接口触发推送

当后端处理好友请求创建的API时,写入数据库成功后立即推送通知:

// 假设已定义FriendRequest和UnreadFriendRequest模型
app.post('/api/send-friend-request', express.json(), async (req, res) => {
  const { fromUserId, toUserId } = req.body;

  try {
    // 写入MongoDB好友请求记录
    const newRequest = await mongoose.model('FriendRequest').create({
      from: fromUserId,
      to: toUserId,
      status: 'pending'
    });

    // 获取目标用户的Socket连接ID
    const targetSocketIds = userSocketMap.get(toUserId);
    if (targetSocketIds?.length) {
      // 向目标用户的所有在线设备推送通知
      targetSocketIds.forEach(socketId => {
        io.to(socketId).emit('new_friend_request', {
          id: newRequest._id,
          from: fromUserId,
          timestamp: newRequest.createdAt
        });
      });
    } else {
      // 如果用户离线,将请求存入未读表
      await mongoose.model('UnreadFriendRequest').create({
        targetUserId: toUserId,
        requestData: newRequest.toObject()
      });
    }

    res.status(201).json({ success: true, request: newRequest });
  } catch (err) {
    res.status(500).json({ success: false, error: err.message });
  }
});

步骤3:React前端监听通知

前端连接Socket.io,登录后发送用户ID,并监听新通知事件:

import { useEffect, useState } from 'react';
import io from 'socket.io-client';

// 连接后端Socket服务
const socket = io('http://localhost:5000');

function FriendNotificationPanel() {
  const [friendRequests, setFriendRequests] = useState([]);
  const userId = localStorage.getItem('authUserId'); // 假设从本地存储获取登录用户ID

  useEffect(() => {
    if (!userId) return;

    // 告知后端当前用户已上线
    socket.emit('user_connected', userId);

    // 监听新好友请求事件
    socket.on('new_friend_request', (request) => {
      setFriendRequests(prev => [request, ...prev]);
      // 可添加弹窗、音效等提醒
      window.alert('收到新的好友请求!');
    });

    // 组件卸载时断开Socket连接
    return () => socket.disconnect();
  }, [userId]);

  return (
    <div className="notification-panel">
      <h3>好友请求</h3>
      {friendRequests.length === 0 ? (
        <p>暂无新请求</p>
      ) : (
        friendRequests.map(req => (
          <div key={req.id} className="request-item">
            用户 {req.from} 申请添加你为好友
          </div>
        ))
      )}
    </div>
  );
}

export default FriendNotificationPanel;

关键优势

  • 零不必要数据库查询:仅在好友请求创建时触发一次写入,无轮询开销
  • 实时性拉满:用户在线时立即收到通知,离线时自动存入未读表,上线后补发
  • 支持多设备:一个用户的多个在线设备都会收到通知

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 19:25:27