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

Node.js集群、Socket.io与Multer结合时文件上传失效问题

解决方案:无需外部中间件的集群兼容配置

1. 修复HAproxy会话粘滞(核心解决点)

HAproxy必须配置Cookie-based会话粘滞,确保同一个客户端的所有请求(包括文件上传和Socket.io连接)都路由到同一个worker进程。这既能保证Multer完整接收表单数据,也能让Socket.io找到对应会话ID。

在HAproxy配置文件中添加以下规则:

frontend http_front
    bind *:80
    cookie SERVERID insert indirect nocache
    default_backend http_back

backend http_back
    balance roundrobin
    cookie SERVERID prefix
    server worker1 127.0.0.1:3001 cookie w1
    server worker2 127.0.0.1:3002 cookie w2
    # 根据实际worker进程数添加更多server条目

通过cookie SERVERID让HAproxy给每个客户端分配标识Cookie,后续请求会自动转发到最初分配的worker。

2. 调整集群进程监听方式

不要让每个worker直接监听端口,改为主进程创建监听套接字后分发给所有worker,实现端口复用的同时配合粘滞策略避免请求拆分。

master.js 修改:

const cluster = require('cluster');
const net = require('net');
const numCPUs = require('os').cpus().length;

if (cluster.isPrimary) {
  // 创建主监听套接字
  const sharedServer = net.createServer();
  sharedServer.listen(3000);

  // 分发给所有worker进程
  for (let i = 0; i < numCPUs; i++) {
    const worker = cluster.fork();
    worker.send('attach-server', sharedServer);
  }

  // 自动重启崩溃的worker
  cluster.on('exit', (worker) => {
    console.log(`worker ${worker.process.pid} 已崩溃,重启中...`);
    const newWorker = cluster.fork();
    newWorker.send('attach-server', sharedServer);
  });
}

worker.js 修改:

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

const app = express();
const upload = multer({ dest: 'uploads/' });

// 导入API路由
const apiRoutes = require('./api');
app.use('/api', apiRoutes(upload));

// 接收主进程分发的套接字并启动服务
process.on('message', (msg, sharedServer) => {
  if (msg === 'attach-server') {
    const httpServer = http.createServer(app);
    // 绑定共享套接字,而非直接监听端口
    httpServer.listen(sharedServer);

    // 初始化Socket.io
    const io = new Server(httpServer, {
      cors: {
        origin: "*",
        methods: ["GET", "POST"]
      }
    });

    io.on('connection', (socket) => {
      console.log(`客户端 ${socket.id} 已连接`);
      // 此处添加Socket.io业务逻辑
    });
  }
});

3. 确保Multer表单解析无干扰

在api.js中,避免在文件上传路由前使用express.json()或express.urlencoded()这类解析器,它们会提前消耗表单数据导致Multer解析失败:

api.js 示例:

module.exports = (upload) => {
  const router = require('express').Router();

  // 直接绑定multer中间件到上传路由
  router.post('/upload', upload.single('file'), (req, res) => {
    if (!req.file) {
      return res.status(400).send('未上传文件');
    }
    res.status(200).send({ filename: req.file.filename, size: req.file.size });
  });

  return router;
};

方案原理说明

  • 会话粘滞:避免同一表单请求被拆分到不同worker,彻底解决Multer的“unexpected end of form”错误。
  • 共享套接字:主进程统一管理端口,worker共享连接,既保证集群负载均衡,又让Socket.io在同一worker内处理客户端连接,消除“Session ID unknown”问题。
  • 无外部依赖:全程无需Redis或PG等中间件,仅通过HAproxy配置和Node.js集群特性解决兼容问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 13:21:09