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

Node.js中Nginx Unit搭配ws包时WebSocket无法接收数据

Node.js WebSocket服务器在Nginx Unit下无法接收数据的解决方法

问题场景

我用Node.js结合Express和ws库搭建了一个同时支持REST接口和WebSocket的HTTP服务器,直接用node app.js启动时,REST和WebSocket功能都正常;但切换到Nginx Unit(版本1.29)部署后,服务器能正常启动,REST接口也能访问,唯独WebSocket无法接收任何数据。

服务器代码

const assert = require('assert');
const Websocket = require('ws');
const express = require('express');
const app = express();
const port = 8080
assert.ok(process.env.WS_SERVER, '必须定义WS_SERVER环境变量');
console.log(`设置记录路径为 ${process.env.WS_SERVER }`);

// 处理WebSocket上传逻辑
async function handleUpload(socket) {
    socket._recvInitialMetadata = false;
    socket.on('message', function(data, isBinary) {
      console.log(data)
      if(socket._recvInitialMetadata) return
      socket._recvInitialMetadata = true
    });

    socket.on('error', function(err) {
      console.error({err}, '流上传发生错误');
    });
    socket.on('close', (data) => {
      console.info({data}, '流已关闭');
    });
    socket.on('end', function(err) {
      console.error({err}, '流上传:socket已关闭');
    });
}

// 初始化WebSocket服务器(不绑定HTTP服务器)
const wsServer = new Websocket.Server({ noServer: true });
wsServer.setMaxListeners(0);
wsServer.on('connection', handleUpload.bind(null));

// 配置Express中间件
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use('/', (req, res) => {
  res.send('Hello World!')
});
app.use((err, req, res, next) => {
  logger.error(err, '服务器错误');
  res.status(err.status || 500).json({msg: err.message});
});

const server = app.listen(port, async () => {
  console.log(`应用监听在 http://localhost:${port}`);
});

// 原生WebSocket升级处理
server.on('upgrade', (request, socket, head) => {
  if(head === undefined) head = new Uint8Array() // 修复Nginx Unit传递head为undefined的问题
  wsServer.handleUpgrade(request, socket, head, (socket) => {
    if (request.url !== process.env.WS_SERVER) return socket.destroy();
    wsServer.emit('connection', socket, request);
  });
});

// 尝试加载unit-http的WebSocket支持
var unitSocketServer = undefined
try {
  require.resolve('unit-http')
  unitSocketServer = require('unit-http/websocket').server({httpServer : server})
  console.log('unit-http websocket服务器已加载')
} catch {

}

Nginx Unit配置(版本1.29)

{
    "listeners": {
        "*:8080": {
            "pass": "applications/app-test"
        }
    },
    "applications": {
        "app-test": {
            "type": "external",
            "working_directory": "/var/www/app",
            "executable": "/usr/bin/env",
            "user": "www-data",
            "group": "www-data",
            "arguments": [
                "node",
                "--loader",
                "unit-http/loader.mjs",
                "--require",
                "unit-http/loader",
                "app.js"
            ],
            "processes": {
                "max" : 12,
                "spare" : 3,
                "idle_timeout" : 30
            }
        }
    }
}

问题根源

  1. 重复的WebSocket处理逻辑:代码同时实现了原生Node.js的upgrade事件监听,又尝试加载unit-http/websocket创建服务器,两者在Nginx Unit环境下产生冲突。Nginx Unit通过unit-http模块接管了HTTP请求的处理,原生的upgrade事件无法正常触发,导致WebSocket连接无法正确建立。
  2. 路径验证逻辑未生效:原本在原生handleUpgrade回调中的URL验证,在Nginx Unit环境下因为事件未触发,根本不会执行,同时unit-http的WebSocket服务器也没有配置对应的路径过滤,导致连接处理异常。

修复方案

核心调整:统一使用unit-http处理WebSocket连接

移除原生的upgrade事件监听,让unit-http的WebSocket服务器直接对接ws库的实例,并在其中完成路径验证。

修正后的关键代码:

// 移除原有的server.on('upgrade')代码块

// 重新初始化unit-http WebSocket服务器
var unitSocketServer = undefined
try {
  const unitWebsocket = require('unit-http/websocket');
  // 配置unit-http的WebSocket服务器,处理升级请求
  unitSocketServer = unitWebsocket.server({httpServer: server}, (request, socket, head) => {
    // 验证请求路径是否匹配
    if (request.url !== process.env.WS_SERVER) {
      socket.destroy();
      return;
    }
    // 将连接交给ws服务器处理
    wsServer.handleUpgrade(request, socket, head || new Uint8Array(), (wsSocket) => {
      wsServer.emit('connection', wsSocket, request);
    });
  });
  console.log('unit-http websocket服务器已加载并配置完成');
} catch (err) {
  console.log('unit-http不可用,降级到原生WebSocket处理');
  // 保留原生逻辑作为 fallback
  server.on('upgrade', (request, socket, head) => {
    if (request.url !== process.env.WS_SERVER) return socket.destroy();
    wsServer.handleUpgrade(request, socket, head || new Uint8Array(), (wsSocket) => {
      wsServer.emit('connection', wsSocket, request);
    });
  });
}

额外检查项

  • 确认项目已安装unit-http依赖:执行npm install unit-http --save
  • 检查Nginx Unit配置中的arguments是否正确,确保unit-http的loader被正确加载
  • 增加日志输出:在handleUpload的message事件中添加更详细的日志,确认消息是否到达

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 13:44:55