Express + WebSocket 实时通信实战指南
Express 不内置 WebSocket,配合 ws 库即可搭建实时通信服务。本文讲解 WebSocket 服务器搭建、连接状态管理、消息广播与心跳保活的生产级写法。
Express 与 ws 库共享同一个 HTTP 服务器即可支持 WebSocket:new WebSocketServer({ server }) 挂在 HTTP server 上,wss.on('connection') 处理连接,ws.send() 发消息,遍历 wss.clients 实现广播。生产环境必须实现心跳机制清理断线连接。
为什么 Express 需要搭档
Express 管 HTTP 请求-响应;WebSocket 是持久双向连接——协议升级(Upgrade 握手)发生在 HTTP 层,之后走自己的帧协议。ws 库接管协议部分,Express 继续管它的路由,两者共用一个端口互不干扰。
搭建 WebSocket 服务器
npm install express ws
const express = require('express');
const { createServer } = require('http');
const { WebSocketServer } = require('ws');
const app = express();
const server = createServer(app);
const wss = new WebSocketServer({ server, path: '/ws' });
wss.on('connection', (ws, req) => {
console.log('新连接来自', req.socket.remoteAddress);
ws.on('message', (data) => {
const msg = JSON.parse(data.toString());
console.log('收到:', msg);
});
ws.on('close', () => console.log('连接关闭'));
ws.on('error', console.error);
ws.send(JSON.stringify({ type: 'welcome' }));
});
server.listen(3000);
客户端连接 ws://localhost:3000/ws 即建立持久连接。
广播与连接管理
// 向所有在线客户端广播
function broadcast(payload) {
const data = JSON.stringify(payload);
wss.clients.forEach((client) => {
if (client.readyState === client.OPEN) {
client.send(data);
}
});
}
群发前必须检查 readyState——向已断开的连接 send 会抛错。管理"房间/频道"时维护 Map<roomId, Set<ws>>,按组投递。
心跳:清理僵尸连接
TCP 半开连接(客户端断电、切网络)不会触发 close 事件,不清理会累积泄漏:
const interval = setInterval(() => {
wss.clients.forEach((ws) => {
if (!ws.isAlive) return ws.terminate(); // 上次心跳无响应,断开
ws.isAlive = false;
ws.ping();
});
}, 30000);
wss.on('connection', (ws) => {
ws.isAlive = true;
ws.on('pong', () => { ws.isAlive = true; });
});
wss.on('close', () => clearInterval(interval));
30 秒一轮 ping/pong,两轮无响应即断开——这是 WebSocket 服务的生产标配。
观察长连接的生命周期
Express + WebSocket 实时通信实战指南上线后,HTTP 握手成功率不能代表消息传输质量。应用需要额外记录在线连接数、消息处理耗时、断连原因和重连次数,再经 DataKit 指标或日志采集接入观测云。APM 用来观察已插桩的握手与消息处理依赖;每条 WebSocket 消息通常需要自行埋点,避免将长连接生命周期直接当作普通请求耗时。
接入文档:Prometheus 指标采集 · 日志采集 · Node.js APM 接入。
常见问题(FAQ)
Q:ws 和 Socket.IO 怎么选?
ws 是轻量原生 WebSocket 实现;Socket.IO 提供自动重连、降级轮询、房间管理、确认回调等开箱功能。简单场景 ws 足够(客户端逻辑自己写);复杂实时应用(聊天室、协作)Socket.IO 省大量轮子。
Q:如何做 WebSocket 认证?
握手时在 query 或 header 携带 token,connection 事件中校验失败即 ws.close(4001, 'Unauthorized');或在 Upgrade 前的 HTTP 中间件里鉴权。JWT 是常用载体。
Q:多实例部署 WebSocket 怎么办?
WebSocket 连接有状态,多实例需要:负载均衡开启会话保持(sticky session),跨实例消息广播用 Redis Pub/Sub 桥接。规模再大考虑专用实时服务(Soketi、Ably 类)。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。