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

Nginx路由中WebSocket请求丢失问题排查求助

WebSocket连接异常排查与修复

问题描述

我正尝试从公网IP的JavaScript应用向Node.js服务器建立WebSocket连接,使用pm2和Nginx做路由转发。虽然WebSocket请求返回200状态码,但服务器日志里完全看不到任何WebSocket相关调用记录,仅能看到服务启动时的端口初始化等日志。

Nginx配置文件(/etc/nginx/sites-available/my_app.com,已软链至sites-enabled)

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream websocket {
   server localhost:3010;
}

upstream backend {
    server localhost:3000;
}

server {
    listen                      80;
    listen                      [::]:80;
    server_name                 my_app.com www.my_app.com --My website ip address for testing--;

    # these are allowed to use unsecured connection...
    location /                  { proxy_pass http://backend; }
     location /ws {
         proxy_pass http://websocket;
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection $connection_upgrade;
         proxy_set_header Host $host;
     }
}

服务器端相关文件

app.js

const express = require("express")
const http = require("http")
const { log } = require("./system/global")
const app = express()
const http_server = http.createServer(app)
const PORT = 3000
require("./system/serve_path")(http_server, 'frontend', app, express)
require("./system/serve_socket").start(PORT)
http_server.listen(PORT, ()=> log("Listening on port " + PORT))

./system/globals.js

const fs = require('fs')
exports.log = (msg) => {
    let now = new Date().toISOString()
    let log_msg = `${now}: ${msg}`
    console.log(log_msg)
    let log_folder = './logs'
    if (!fs.existsSync(log_folder)) { fs.mkdirSync(log_folder) }
    let log_file = `${log_folder}/log.log`
    if (!fs.existsSync(log_file)) {
        fs.writeFileSync(log_file, '') 
    } else {
        let stats = fs.statSync(log_file)
        let fileSize = stats.size
        if (fileSize > 1073741824) {
            let now = new Date().toISOString()
            let new_file_name = `${log_folder}/log_${now}.log`
            fs.renameSync(log_file, new_file_name)
            fs.writeFileSync(log_file, '')
        }
    }
    fs.appendFile(log_file, '\n' + log_msg, (err) => {
        if (err) { console.log(err) }
    })
}
exports.load_system = (module_name) => {
    return require(`./system/${module_name}`)
}
exports.load_socket = (module_name) => {
    return require(`./socket/${module_name}`)
}

./system/serve_path.js

module.exports = (server, path, app, express) => {
    app.use(express.static(path))
}

./system/serve_socket.js

const websocket = require('ws')
const { log } = require('./global')
exports.start = (port) => {
    let socket_port = port + 10
    log("Socket started on port: " + socket_port)
    let wss = new websocket.Server({port: socket_port})
    wss.on('connection', function(ws) {
        log('Socket connected')
        ws.on('open', on_message)
        ws.on('error', on_message)
        ws.on('message', on_message)
    })
}
const on_message = (msg) => {
    console.log('Received from client: %s', msg);
    log('Socket Message: %s', msg);
    ws.send('Socket Message: ' + msg);
}
const on_error = (err) => { log('Socket Error: %s', err); }
const on_open = () => { log('Socket connection opened'); }

问题排查与修复

1. 事件绑定与作用域错误

serve_socket.js存在两个关键问题:

  • 将open、error、message事件全部绑定到on_message函数,逻辑混乱;
  • on_message中引用的ws变量不在当前作用域,会导致报错。

修复后的serve_socket.js:

const websocket = require('ws')
const { log } = require('./global')

exports.start = (port) => {
    let socket_port = port + 10
    log("Socket started on port: " + socket_port)
    let wss = new websocket.Server({port: socket_port})
    
    wss.on('connection', function(ws) {
        log('Socket connected')
        
        // 绑定对应事件到正确处理函数,传递ws实例
        ws.on('message', on_message.bind(null, ws))
        ws.on('error', on_error.bind(null, ws))
        ws.on('close', () => log('Socket connection closed'))
    })
}

const on_message = (ws, msg) => {
    console.log('Received from client: %s', msg);
    log('Socket Message: %s', msg);
    ws.send('Socket Message: ' + msg);
}

const on_error = (ws, err) => { 
    log('Socket Error: %s', err); 
    ws.close()
}

2. 客户端连接路径验证

确保客户端WebSocket连接地址为ws://your-domain/ws,而非直接访问3010端口——Nginx已经做了反向代理,直接访问端口可能被防火墙或Nginx拦截。

3. Nginx配置优化

在location /ws中添加超时与转发头配置,避免连接被意外断开:

location /ws {
    proxy_pass http://websocket;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    # 长连接超时设置
    proxy_connect_timeout 7d;
    proxy_send_timeout 7d;
    proxy_read_timeout 7d;
}

4. 验证步骤

  1. 重启Nginx:sudo nginx -s reload
  2. 重启Node.js服务:pm2 restart your-app-name
  3. 查看服务器日志,确认是否有Socket connected日志输出
  4. 检查客户端控制台,确认无连接错误且连接URL正确

内容的提问来源于stack exchange,提问作者Michael Tot Korsgaard

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:45:17