Socket.io 意外断开连接 [英] Socket.io disconnected unexpectedly

查看:32
本文介绍了Socket.io 意外断开连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有 node.js 服务和 Angular 客户端,使用 socket.io 在长时间的 http 请求中传输一些消息.

I have node.js service and angular client using socket.io to transport some message during long time http request.

服务:

export const socketArray: SocketIO.Socket[] = [];
export let socketMapping: {[socketId: string]: number} = {};

const socketRegister: hapi.Plugin<any> = {
    register: (server) => {
        const io: SocketIO.Server = socket(server.listener);

        // Whenever a session connected to socket, create a socket object and add it to socket array
        io.on("connection", (socket) => {
            console.log(`socket ${socket.id} connected`);
            logger.info(`socket ${socket.id} connected`);

            // Only put socket object into array if init message received
            socket.on("init", msg => {
                logger.info(`socket ${socket.id} initialized`);
                socketArray.push(socket);
                socketMapping[socket.id] = msg;
            });

            // Remove socket object from socket array when disconnected
            socket.on("disconnect", (reason) => {
                console.log(`socket ${socket.id} disconnected because: ${reason}`)
                logger.info(`socket ${socket.id} disconnected because: ${reason}`);
                for(let i = 0; i < socketArray.length; i ++) {
                    if(socketArray[i] === socket) {
                        socketArray.splice(i, 1);
                        return;
                    }
                }
            });
        });
    },
    name: "socketRegister",
    version: "1.0"
}

export const socketSender = async (socketId: string, channel: string, content: SocketMessage) => {
    try {
        // Add message to db here
        // await storeMessage(socketMapping[socketId], content);
        // Find corresponding socket and send message
        logger.info(`trying sending message to ${socketId}`);
        for (let i = 0; i < socketArray.length; i ++) {
            if (socketArray[i].id === socketId) {
                socketArray[i].emit(channel, JSON.stringify(content));
                logger.info(`socket ${socketId} send message to ${channel}`);
                if (content.isFinal == true) {
                    // TODO: delete all messages of the process if isFinal is true
                    await deleteProcess(content.processId);
                }
                return;
            }
        }
    } catch (err) {
        logger.error("Socket sender error: ", err.message);
    }

};

客户:

connectSocket() {
   if (!this.socket) {
       try {
           this.socket = io(socketUrl);
           this.socket.emit('init', 'some-data');
       } catch (err) {
           console.log(err);
       }
   } else if (this.socket.disconnected) {
       this.socket.connect();
       this.socket.emit('init', 'some-data');
   }
   this.socket.on('some-channel', (data) => {
       // Do something
   });
   this.socket.on('disconnect', (data) => {
       console.log(data);
   });

}

它们通常工作正常,但随机产生断开连接错误.从我的日志文件中,我们可以看到:

They usually work fine but produce disconnection error randomly. From my log file, we can see this:

2018-07-21T00:20:28.209Z[x]INFO: socket 8jBh7YC4A1btDTo_AAAN connected

2018-07-21T00:20:28.324Z[x]INFO: socket 8jBh7YC4A1btDTo_AAAN initialized

2018-07-21T00:21:48.314Z[x]INFO: socket 8jBh7YC4A1btDTo_AAAN disconnected because: ping timeout

2018-07-21T00:21:50.849Z[x]INFO: socket C6O7Vq38ygNiwGHcAAAO connected

2018-07-21T00:23:09.345Z[x]INFO: trying sending message to C6O7Vq38ygNiwGHcAAAO

并且在断开消息的同时,前端也注意到一个断开事件,它说transport close.

And at the same time of disconnect message, front-end also noticed a disconnect event which saying transport close.

从日志中,我们可以得到工作流程是这样的:

From the log, we can get the work flow is this:

  1. 前端启动了一个套接字连接并向后端发送了一个初始化消息.它还节省了套接字.
  2. 后端检测到连接并收到初始化消息
  3. 后端将socket放入数组,方便随时随地使用
  4. 第一个套接字意外断开连接,另一个连接在前端不知情的情况下发布,因此前端从不发送消息来初始化它.
  5. 由于前端保存的套接字没有改变,所以它在发出 http 请求时使用旧的套接字 ID.结果,后端使用已从套接字数组中删除的旧套接字发送消息.

这种情况并不经常发生.有谁知道可能导致断开连接和未知连接问题的原因?

The situation doesn't happen frequently. Does anyone know what could cause the disconnect and unknown connect issue?

推荐答案

这实际上取决于长时间的 http 请求"在做什么.node.js 将您的 Javascript 作为单线程运行.这意味着它一次只能做一件事.但是,由于服务器所做的许多事情都与 I/O 相关(从数据库读取、从文件中获取数据、从另一台服务器获取数据等...)并且 node.js 使用事件驱动的异步 I/O,它通常可以同时在空中放置许多球,因此它似乎同时处理大量请求.

It really depends what "long time http request" is doing. node.js runs your Javascript as a single thread. That means it can literally only do one thing at a time. But, since many things that servers do are I/O related (read from a database, get data from a file, get data from another server, etc...) and node.js uses event-driven asynchronous I/O, it can often have many balls in the air at the same time so it appears to be working on lots of requests at once.

但是,如果您的复杂 http 请求是 CPU 密集型的,使用大量 CPU,那么它会占用单个 Javascript 线程,并且在占用 CPU 时无法完成任何其他操作.这意味着所有传入的 HTTP 或 socket.io 请求必须在队列中等待,直到一个 node.js Javascript 线程空闲,以便它可以从事件队列中获取下一个事件并开始处理传入的请求.

But, if your complex http request is CPU-intensive, using lots of CPU, then it's hogging the single Javascript thread and nothing else can get done while it is hogging the CPU. That means that all incoming HTTP or socket.io requests have to wait in a queue until the one node.js Javascript thread is free so it can grab the next event from the event queue and start to process that incoming request.

如果我们能看到这个非常复杂的 http 请求"的代码,我们才能真正为您提供更具体的帮助.

We could only really help you more specifically if we could see the code for this "very complex http request".

在 node.js 中解决 CPU 占用问题的常用方法是将 CPU 密集型的东西卸载到其他进程.如果主要是这段代码导致了问题,您可以启动多个子进程(可能与您服务器中的 CPU 数量一样多),然后为它们提供 CPU 密集型工作并离开您的主节点.js 进程可以自由处理传入(非 CPU 密集型)请求,延迟极低.

The usual way around CPU-hogging things in node.js is to offload CPU-intensive stuff to other processes. If it's mostly just this one piece of code that causes the problem, you can spin up several child processes (perhaps as many as the number of CPUs you have in your server) and then feed them the CPU-intensive work and leave your main node.js process free to handle incoming (non-CPU-intensive) requests with very low latency.

如果您有多个可能占用 CPU 的操作,那么您要么必须将它们全部分配给子进程(可能通过某种工作队列),或者您可以部署集群.集群的挑战在于,给定的 socket.io 连接将连接到集群中的一个特定服务器,如果该进程恰好正在执行占用 CPU 的操作,那么分配给该服务器的所有 socket.io 连接都将有不好的延迟.因此,对于此类问题,常规聚类可能不太好.用于处理 CPU 密集型工作的工作队列和多个专门的子进程可能会更好,因为这些进程不会有任何它们负责的外部 socket.io 连接.

If you have multiple operations that might hog the CPU, then you either have to farm them all out to child processes (probably via some sort of work queue) or you can deploy clustering. The challenge with clustering is that a given socket.io connection will be to one particular server in your cluster and if it's that process that just happens to be executing a CPU-hogging operation, then all the socket.io connections assigned to that server would have bad latency. So, regular clustering is probably not so good for this type of issue. The work-queue and multiple specialized child processes to handle CPU-intensive work are probably better because those processes won't have any outside socket.io connections that they are responsible for.

此外,您应该知道,如果您使用的是同步文件 I/O,则会阻塞整个 node.js Javascript 线程.node.js 在同步文件 I/O 操作期间不能运行任何其他 Javascript.node.js 从其异步 I/O 模型中获得了可扩展性和同时进行许多操作的能力.如果您使用同步 I/O,则会完全破坏它并破坏可扩展性和响应能力.

Also, you should know that if you're using synchronous file I/O, that blocks the entire node.js Javascript thread. node.js can not run any other Javascript during a synchronous file I/O operation. node.js gets its scalability and its ability to have many operations in flight at the same from its asynchronous I/O model. If you use synchronous I/O, you completely break that and ruin scalability and responsiveness.

同步文件 I/O 只属于服务器启动代码或单一用途的脚本(不是服务器).在服务器中处理请求时不应使用它.

Synchronous file I/O belongs only in server startup code or in a single purpose script (not a server). It should never be used while processing a request in a server.

使异步文件 I/O 更容易接受的两种方法是使用流或使用带有承诺的 fs 方法的 async/await.

Two ways to make asynchronous file I/O a little more tolerable are by using streams or by using async/await with promisified fs methods.

这篇关于Socket.io 意外断开连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆