NextJS+socket.io+PM2部署后WebSocket连接建立前关闭报错
问题背景
首次接触Socket相关开发,项目技术栈为 NextJS(v10)、Express、socket.io、socket.io-client、freeice。本地localhost环境运行完全正常,使用PM2部署到AWS测试服务器后出现连接异常,服务端connection事件监听完全无法触发,客户端短时间内抛出报错:
WebSocket connection to '' failed: WebSocket is closed before the connection is established.
涉及的核心代码如下:
服务端代码(server.ts)
const nextApp = next({ dev: config.dev }); const app = express(); const server = require('http').Server(app); const io = require('socket.io')(server); io.on('connection', socket => console.log('connected')); // 其余代码暂未贴出,当前连这行连接日志都无法触发
客户端代码
import { io } from 'socket.io-client'; const options = { 'force_new_connection': true, reconnectionAttempts: 'Infinity', timeout: 10000, transports: ['websocket'] }; const socket = io('https://mysite.com:3000', options);
问题答疑与解决方案
1. AWS服务器配置与PM2对WebSocket连接的影响
- AWS服务器默认不会拦截WebSocket连接,但需要检查两类配置:
- 安全组规则:确认服务使用的3000端口入站规则已开放,允许客户端IP访问对应TCP端口。
- 前置代理配置:如果服务前挂载了Nginx、AWS ALB负载均衡,必须在代理层开启WebSocket支持,配置
Upgrade、Connection头的转发规则,否则代理层会直接拦截WebSocket升级请求,导致握手失败。
- PM2本身不会阻断WebSocket连接,但两个常见配置错误会引发连接异常:
- 集群模式未配置会话粘性:WebSocket是长连接,PM2集群模式下如果没有开启sticky session,请求会被轮询转发到不同服务进程,直接导致握手失败;单实例启动模式不存在该问题。
- 端口监听异常:确认PM2启动的服务正常监听3000端口,没有出现端口被其他进程占用、服务启动失败的情况,可通过
netstat -tunlp | grep 3000命令检查端口监听状态。
2. 服务端const server = require('http').Server(app);写法说明
这行代码本身不存在语法错误,但90%的同类部署问题都是因为配套的启动逻辑写错:必须使用挂载了Socket.IO的这个server实例监听端口,不能用Express的app实例直接调用listen方法。
很多开发者写Next+Express项目时会习惯写app.listen(3000)启动服务,该方法会创建一个全新的独立HTTP服务实例,你挂载Socket.IO的server实例根本没有被启动、没有监听任何端口,自然收不到任何连接请求。本地开发时Next dev模式可能自动处理了启动逻辑绕过了该问题,生产环境手动写启动逻辑就会触发该bug。
正确的启动写法参考:
// 错误写法:app.listen会创建新的HTTP服务实例,和挂载io的server不是同一个 // app.listen(3000) // 正确写法:使用挂载了io的server实例监听端口 server.listen(3000, () => { console.log('服务已启动,监听3000端口') nextApp.prepare().then(() => { // 写入Next路由处理、接口逻辑 }) })
3. Socket实例lt属性说明
socket.io-client在生产环境构建时会压缩混淆内部属性名,lt是库内部用于存储连接超时计时器的私有属性,本地和线上环境属性名不一致是因为两个环境的打包压缩策略、依赖小版本存在差异,属于正常现象,不是导致连接失败的原因,无需额外处理。
额外排查点
- 检查客户端连接地址是否存在拼写错误,比如协议名和域名之间多余的空格,会直接导致地址解析失败;如果站点配置了HTTPS,要确认证书有效,使用
wss协议建立连接,避免HTTPS站点下非加密ws连接被浏览器安全策略拦截。 - 修正客户端配置错误:
reconnectionAttempts参数需要传入数字类型Infinity,当前传入的字符串'Infinity'属于无效配置,会导致重连逻辑异常。 - 测试阶段可以先把客户端
transports配置改成['websocket', 'polling'],先降级用长轮询模式测试连通性,确认基础网络链路正常后再强制走WebSocket协议。 - 检查服务器系统防火墙(如firewalld、ufw)是否开放3000端口的外部访问权限。
内容的提问来源于stack exchange,提问作者Roman Nozhenko

