Nodejs socket.io 在多线程下怎么通信

Nodejs socket.io 在多线程下怎么通信

在用socket.io做web聊天系统的时候为了解决nodejs的单线程易崩溃的状况,使用cluster.fork()多开了些线程,有此带来的问题是每一个线程都有自己的socket,多个浏览器测试发现有的时候能进入同一个进程就可以通信,没有进入的话后台狂打印信息 debug - cleared heartbeat interval for client xDbxAhAOkoQHfQWC7TGT debug - discarding transport debug - client authorized info - handshake authorized wGNiKiQxge2j1tpQ7TGV debug - client authorized info - handshake authorized A50oRdQZNg27S2Hr7TGW debug - setting request GET /socket.io/1/websocket/wGNiKiQxge2j1tpQ7TGV debug - set heartbeat interval for client wGNiKiQxge2j1tpQ7TGV debug - websocket writing 7:::1+0 warn - client not handshaken client should reconnect info - transport end (error) debug - set close timeout for client wGNiKiQxge2j1tpQ7TGV debug - cleared close timeout for client wGNiKiQxge2j1tpQ7TGV debug - cleared heartbeat interval for client wGNiKiQxge2j1tpQ7TGV debug - discarding transport debug - client authorized info - handshake authorized BmULkJ6v4etxDReY7TGX debug - setting request GET /socket.io/1/websocket/BmULkJ6v4etxDReY7TGX debug - set heartbeat interval for client BmULkJ6v4etxDReY7TGX debug - websocket writing 7:::1+0 warn - client not handshaken client should reconnect info - transport end (error) 一直不停打印,想知道是为什么?以及解决问题的思路? 有一个想法是把socket只写到主线程里,就是写到一个线程里,其他线程不管,看这样想是否正确呢?


8 回复

Node.js Socket.IO 在多线程下的通信问题

在使用 Node.js 和 Socket.IO 构建 Web 聊天系统时,为了提高性能和避免单线程瓶颈,我们通常会使用 cluster 模块来创建多个工作进程。然而,这会导致每个进程都有独立的 Socket 连接,从而引发通信问题。

问题描述

当使用 cluster.fork() 创建多个工作进程时,每个进程都会独立地处理 Socket 连接。这意味着客户端与某个工作进程建立连接后,无法与其他工作进程通信。这导致了以下问题:

  • 频繁的重连:由于客户端无法在不同进程之间切换,它们会不断尝试重新连接。
  • 日志错误:控制台中会打印大量的错误信息,如心跳超时、传输丢弃等。

示例代码

假设我们有一个简单的聊天应用,使用 cluster 模块来分发负载。

const cluster = require('cluster');
const http = require('http');
const socketIo = require('socket.io');

if (cluster.isMaster) {
    // 主进程
    console.log(`主进程运行在 PID ${process.pid}`);
    for (let i = 0; i < 2; i++) {
        cluster.fork();
    }
} else {
    // 工作进程
    const server = http.createServer();
    const io = socketIo.listen(server);

    io.on('connection', (socket) => {
        console.log(`客户端连接: ${socket.id}`);

        socket.on('chat message', (msg) => {
            console.log(`收到消息: ${msg}`);
            socket.broadcast.emit('chat message', msg); // 广播给其他客户端
        });

        socket.on('disconnect', () => {
            console.log(`客户端断开连接: ${socket.id}`);
        });
    });

    server.listen(3000, () => {
        console.log(`工作进程 ${process.pid} 监听在端口 3000`);
    });
}

解决方案

  1. 集中管理 Socket 连接:将所有 Socket 连接集中到一个特定的工作进程(例如主进程)中,其他工作进程则负责业务逻辑。

  2. 进程间通信:使用 process.sendprocess.on('message') 实现进程间的通信,将消息转发到正确的进程。

if (cluster.isMaster) {
    console.log(`主进程运行在 PID ${process.pid}`);
    for (let i = 0; i < 2; i++) {
        cluster.fork();
    }

    cluster.on('exit', (worker, code, signal) => {
        console.log(`工作进程 ${worker.process.pid} 退出`);
    });

    let workerIndex = 0;
    function getNextWorker() {
        workerIndex = (workerIndex + 1) % cluster.workers.length;
        return cluster.workers[Object.keys(cluster.workers)[workerIndex]];
    }

    io.on('connection', (socket) => {
        console.log(`客户端连接: ${socket.id}`);

        socket.on('chat message', (msg) => {
            console.log(`收到消息: ${msg}`);
            const worker = getNextWorker();
            worker.send('chat message', { socketId: socket.id, message: msg });
        });

        socket.on('disconnect', () => {
            console.log(`客户端断开连接: ${socket.id}`);
        });
    });
} else {
    process.on('message', (msg, workerSocketId) => {
        if (msg === 'chat message') {
            io.to(workerSocketId).emit('chat message', worker.message);
        }
    });
}

通过上述方法,我们可以有效地解决多线程环境下 Socket.IO 的通信问题,并确保客户端能够正常通信。


可以使用 pomelo

需要这么复杂啊,不懂哦

楼主,Node cluster这叫多进程。

socket.io 可以通过redis 来进行进程/集群之间的状态和信息共享。在configuring socket.io里面的store这段里面有描述

分享一下文章,Cloud Foundry 是這樣達到多綫線溝通的

主要看下第三部份,把 PubSub 放到 Redis 去做 因為提到多綫程,想必也要做相關的配置,這裏說的比較清晣,我們也這配置這個環境。

http://blog.cloudfoundry.com/2013/01/24/scaling-real-time-apps-on-cloud-foundry-using-node-js-and-redis/

是你的多进程之间状态没有共享吧?

socket一旦连接,就是固定与客户端相连的。

在使用 socket.io 进行 Web 聊天系统的开发时,如果通过 cluster.fork() 开启了多线程,那么每个线程(即工作进程)都会有自己的 socket 服务器实例。这会导致不同的客户端连接到不同的线程,从而无法实现跨线程间的直接通信。

解决方案

解决这一问题的方法是在主进程中集中处理所有 socket.io 的通信,然后将消息转发给相应的子进程。下面是具体步骤:

  1. 主进程管理 socket.io:所有的 socket 连接都在主进程中创建和维护。
  2. 子进程接收消息:当主进程收到消息后,根据消息内容选择合适的子进程进行消息转发。
  3. 消息传递机制:可以通过进程间通信(IPC)来实现在主进程与子进程之间的数据交换。

示例代码

主进程代码

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
const io = require('socket.io')(server);

if (cluster.isMaster) {
    // Fork workers.
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    const workers = [];

    cluster.on('online', function(worker) {
        console.log(`Worker ${worker.process.pid} is online`);
        workers.push(worker);
    });

    io.on('connection', function(socket) {
        // 根据需要分配 socket 连接到特定的工作进程
        let workerIndex = socket.id % numCPUs;
        let worker = workers[workerIndex];
        console.log(`Socket ${socket.id} assigned to Worker ${worker.process.pid}`);

        // 建立 IPC 通道
        worker.send('socket', socket.id);
        worker.on('message', function(msg, workerId) {
            console.log(`Message from worker ${workerId}:`, msg);
            io.to(socket.id).emit('response', msg);
        });
    });
}

子进程代码

process.on('message', function(m, socketId) {
    if (m === 'socket') {
        // 监听来自主进程的消息
        io.sockets.socket(socketId).on('message', function(data) {
            // 处理数据并发送响应
            this.emit('response', data);
        });
    }
});

总结

通过上述方法,我们可以在 Node.js 中使用 socket.io 和多线程来实现跨线程的通信。关键在于让主进程管理所有 socket 连接,并利用 IPC 将消息分发到正确的子进程中。

回到顶部