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

Express+Socket.IO下如何无延迟处理并发请求?

问题本质

Node.js 基于单线程事件循环模型运行,Socket.IO的事件处理默认在主线程执行。如果某个事件处理(比如遍历大量记录并持续调用socket.emit)占用主线程时间过长,会直接阻塞事件循环——后续的请求或事件只能等当前耗时任务完成后才能被处理,这就是你遇到的延迟问题。

解决办法

1. 拆分大响应为分批发送

别一次性把所有记录砸给客户端,拆成多个小批次发送,每发完一批就用setImmediate让出事件循环,给其他请求腾位置。

服务器端示例:

socket.on('get-large-data', async () => {
  // 假设这是获取海量数据的异步方法
  const allRecords = await fetchAllLargeRecords();
  const batchSize = 100; // 每批发100条
  let current = 0;

  function sendNextBatch() {
    if (current >= allRecords.length) {
      socket.emit('large-data-done'); // 告诉客户端全部发完了
      return;
    }
    const batch = allRecords.slice(current, current + batchSize);
    socket.emit('large-data-batch', batch);
    current += batchSize;
    setImmediate(sendNextBatch); // 让出主线程,处理其他请求
  }

  sendNextBatch();
});

客户端只需要监听large-data-batch接收分批数据,最后监听large-data-done确认完成即可。

2. 用Worker Threads隔离耗时任务

把数据查询、格式化这类耗时操作丢到Node.js的Worker Threads(子线程)里执行,主线程只负责Socket.IO的消息收发,完全不被阻塞。

先写一个子线程文件data-worker.js:

const { parentPort } = require('worker_threads');

parentPort.on('message', async (task) => {
  if (task.type === 'fetch-big-data') {
    // 这里做耗时的数据处理
    const processedData = await heavyDataProcessing();
    parentPort.postMessage({ type: 'result', data: processedData });
  }
});

主线程里调用:

const { Worker } = require('worker_threads');

socket.on('get-large-data', () => {
  const worker = new Worker('./data-worker.js');
  worker.postMessage({ type: 'fetch-big-data' });
  
  worker.on('message', (res) => {
    // 拿到子线程处理好的数据后,再发给客户端(也可以继续分批发)
    socket.emit('large-data', res.data);
    worker.terminate();
  });
  
  worker.on('error', (err) => {
    socket.emit('data-error', err.message);
    worker.terminate();
  });
});

3. 优化数据传输格式

如果传输的是结构化数据,用二进制序列化格式(比如MessagePack、Protocol Buffers)替代JSON,能大幅减少数据体积和序列化/反序列化的耗时,间接降低主线程的负载。

示例:用socket.io-msgpack-parser替换默认的JSON解析器

// 服务器端
const { Server } = require('socket.io');
const msgpackParser = require('socket.io-msgpack-parser');

const io = new Server(httpServer, {
  parser: msgpackParser
});
// 客户端也要对应引入并使用该解析器

4. 绝对避免同步耗时操作

所有涉及IO的操作(数据库查询、文件读写)必须用异步API,绝对不能用同步版本(比如fs.readFileSync、db.querySync)——同步操作会直接卡死事件循环,所有请求都会被堵死。

内容的提问来源于stack exchange,提问作者Parisa Seif

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 08:42:36