使用 mern 堆栈和 socket.io 的聊天应用程序在发送超过 20 条消息后变慢

Posted

技术标签:

【中文标题】使用 mern 堆栈和 socket.io 的聊天应用程序在发送超过 20 条消息后变慢【英文标题】:chat app using mern stack and socket.io getting slow after sending more than 20 messages 【发布时间】:2021-06-14 06:59:40 【问题描述】:

我正在使用 MERN 堆栈和 socket.io 构建一个实时聊天应用程序。发送大约 20 条消息后,应用程序变慢,当其他用户仅在收到发送的消息后才发送另一条消息时,用户无法编写和发送消息。 这是我的聊天组件

import React, useEffect, useRef, useState from 'react';
import Messages from "../Messages/Messages";
import axios from 'axios';
import io from "socket.io-client";

const socket = io('http://localhost:5000/')
function Chat(props) 
    const [messages, setMessages] = useState([])
    const [input, setInput] = useState("")
    useEffect(() => 
        axios.get("http://localhost:5000/")
            .then((res) => 
                setMessages(res.data)
            )
           
        return () => 
            socket.disconnect();
        
    , [])
    useEffect(() => 

        socket.on('msgSent', (data) => 
            setMessages([...messages, data])
        )
    , [messages])
    const renderMessages = () => 
        return (
            messages.map( (message) => 
                return (
                    <Messages key=message._id sender=message.sender.firstName.concat(message.sender.lastName)
                              messageText=message.messageText
                               />
                )
            )
        )
    
    const sendMessage = (e) => 
        e.preventDefault()
        const message = 
            messageText: input,
        
        axios.post("http://localhost:5000/", message)
            .then(res => 
                setMessages([...messages, res.data])
                socket.emit('msgSent', res.data)
            )         
    
    return (       
                renderMessages()
                <form onSubmit=(e) => sendMessage(e)>
                    <input type="text" placeholder="type a message" value=input
                           onChange=(e) => setInput(e.target.value)/>
                    <button type="submit"></button>
                </form>                
    );

export default Chat;

这是我的服务器端代码

const express = require("express")
const app = express()
const mongoose = require('mongoose')
const homeRoot = require("./Routes/homeRoot")
const http = require('http').Server(app);
const io = require('socket.io')(http)    
//listening
http.listen(5000, () => 
    console.log("hello from server")
)

//Routes
app.use("/", homeRoot)

//socket
io.on('connection', (socket) => 
    socket.on('msgSent',(data)=>
        socket.broadcast.emit('msgSent',data)
    )
);

//Db connection
mongoose.connect("mongodb+srv://@cluster0.agiog.mongodb.net/myFirstDatabase?retryWrites=true&w=majority")

这是我正在获取消息的 homeRoot

const express = require("express")
const Router = express.Router()
const Messages = require("../Models/messageModel")
const Users = require("../Models/userModel")
const auth = require("../auth")
Router.get("/",auth,async(req,res)=>
    try 
        const messages = await Messages.find().populate("sender","firstName lastName")
        res.status(200).send(messages)
    catch (e) 
        res.status(404).send("messages not found")
    
)

Router.post("/",auth,async(req,res)=>

    const user = await Users.findOne(_id:req.userId)
    const message = new Messages(...req.body,sender:user)
    try 
        const savedMessage = await message.save()
        res.status(201).send(savedMessage)

    catch (e) 
        res.status(404).send("error occured")
    
)

module.exports = Router

【问题讨论】:

感谢您回答我的问题,我是 SO 新手,尝试编辑我的问题,我希望现在更清楚了,问题是用户只能在收到即将发布的消息后才能编写和发送消息消息,这会导致应用程序变慢 谢谢,清楚多了。但是,我仍然不确定发布数据以及通过套接字发送数据的基本原理是什么。我会跳过 POST 并让套接字处理程序将消息放入数据库。跳过 GET 并让套接字处理程序接收来自广播的消息。您当前的安排就像给某人发短信和写具有相同内容的信件 - 只需发短信并跳过蜗牛邮件。 【参考方案1】:
useEffect(() => 
   socket.on('msgSent', (data) => 
   setMessages([...messages, data])
  )

return
 socket.off("msgSent");
 
, [messages])

你需要在上面的例子中像这样使用 socket.off()。

【讨论】:

【参考方案2】:

Yadnesh 给出的答案有一个小陷阱。让我解释一下。

首先让我们看看为什么你的代码有问题?

您已将 socket.on 侦听器直接放置在组件中,而不是将其放置在任何钩子中。这意味着每次您的组件重新渲染时都会创建一个新的 socket.on 侦听器。 您的组件已安装 -> 您将拥有一个 socket.on 监听器 您收到第一条消息 -> 以前的侦听器仍然存在,但由于组件重新渲染,将创建一个新的侦听器 您收到第二条消息 -> 前两个监听器组合现在将产生总共 4 个监听器

这种模式还在继续..

您可能很想使用下面给出的代码,但它的方法是错误的。

useEffect(() => 
  socket.on('msgSent', (data) => 
   setMessages([...messages, data])
  );

  return () => 
    socket.off("msgSend");
  ;
, [messages]);

使用 [messageList] 作为 useEffect 的第二个参数的问题??

问题是您不想在每次更新 messageList 时都创建新的 socket.io 侦听器。在组件的生命周期中,您应该始终只为一个事件创建一个侦听器。因为一旦创建了 socket.on 侦听器,它就不会在侦听该事件的第一次发生后被销毁,它只是再次侦听该事件。

所以这样的监听器必须创建一次。在您的情况下,它应该在组件安装时创建。

因此使用[],空列表作为useEffect 的第二个参数。这个钩子相当于componentDidMount。

正确的代码

useEffect(() => 
  socket.on('msgSent', (data) => 
   setMessages([...messages, data])
  );

  return () => 
    socket.off("msgSend");
  ;
, []);

【讨论】:

以上是关于使用 mern 堆栈和 socket.io 的聊天应用程序在发送超过 20 条消息后变慢的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Socket.io 中与 Heroku 服务器建立 Socket 连接?

在 API 端使用 Ionic 和 laravel 进行实时聊天

使用 Socket.IO 和 NodeJS 实现音频聊天

使用 node.js、websockets 和 socket.io 创建实时聊天

使用 Socket.Io 聊天应用程序 - 发送和接收消息之间的区别

使用 node.js、socket.io 和 redis 的一对一聊天应用程序