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 } } } }
问题根源
- 重复的WebSocket处理逻辑:代码同时实现了原生Node.js的
upgrade事件监听,又尝试加载unit-http/websocket创建服务器,两者在Nginx Unit环境下产生冲突。Nginx Unit通过unit-http模块接管了HTTP请求的处理,原生的upgrade事件无法正常触发,导致WebSocket连接无法正确建立。 - 路径验证逻辑未生效:原本在原生
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
相关产品推荐
相关产品推荐

