Fastify WebSocket 实战:@fastify/websocket 完全指南
Fastify 通过官方 @fastify/websocket 插件支持 WebSocket。本文讲解插件注册、路由级 WebSocket handler、连接错误处理、消息广播与心跳管理。
Fastify 的 WebSocket 支持来自官方插件 @fastify/websocket:注册后在路由中声明 websocket: true,handler 第一个参数拿到 socket 对象即可收发消息。WebSocket 路由与普通路由共存于同一实例,享受 Fastify 的插件体系与日志集成。
安装与注册
npm install fastify @fastify/websocket
const fastify = require('fastify')({ logger: true });
fastify.register(require('@fastify/websocket'));
fastify.register(async function (app) {
app.get('/ws', { websocket: true }, (socket, req) => {
socket.on('message', (data) => {
const msg = JSON.parse(data.toString());
app.log.info({ msg }, '收到消息');
});
socket.send(JSON.stringify({ type: 'welcome' }));
});
});
fastify.listen({ port: 3000 });
{ websocket: true } 把该路由升级为 WebSocket 端点;客户端连接 ws://localhost:3000/ws。
连接错误处理
WebSocket 的错误有两层:协议层错误(socket 'error' 事件)与业务层异常(handler 内 throw)。两者都要接住:
app.get('/ws', { websocket: true }, (socket, req) => {
socket.on('error', (err) => {
req.log.error({ err }, 'WebSocket 连接错误');
});
socket.on('message', (data) => {
try {
const msg = JSON.parse(data.toString());
handleMessage(socket, msg);
} catch (err) {
socket.send(JSON.stringify({ type: 'error', message: '消息格式非法' }));
}
});
socket.on('close', () => cleanup(socket));
});
单个连接的异常绝不能拖垮进程——message handler 里的 try/catch 是底线。
广播与客户端管理
const clients = new Set();
app.get('/ws', { websocket: true }, (socket, req) => {
clients.add(socket);
socket.on('close', () => clients.delete(socket));
});
function broadcast(payload) {
const data = JSON.stringify(payload);
for (const client of clients) {
if (client.readyState === 1) { // OPEN
client.send(data);
}
}
}
封装成插件把 clients 集合注入 Fastify 实例(fastify.decorate('broadcast', broadcast)),HTTP 路由里也能触发广播——"管理后台操作 → 实时推给所有在线用户"就此打通。
心跳与生产要点
与原生 ws 相同的心跳模式:每 30 秒 ping、标记 isAlive、两轮无响应 terminate。生产部署注意:Nginx 反代要配置 Upgrade 头转发(proxy_set_header Upgrade $http_upgrade);多实例广播需要 Redis Pub/Sub 桥接;连接数指标纳入监控。
观察长连接的生命周期
Fastify WebSocket 实战上线后,HTTP 握手成功率不能代表消息传输质量。应用需要额外记录在线连接数、消息处理耗时、断连原因和重连次数,再经 DataKit 指标或日志采集接入观测云。APM 用来观察已插桩的握手与消息处理依赖;每条 WebSocket 消息通常需要自行埋点,避免将长连接生命周期直接当作普通请求耗时。
接入文档:Prometheus 指标采集 · 日志采集 · Node.js APM 接入。
常见问题(FAQ)
Q:@fastify/websocket 和 Socket.IO 怎么选?
前者是原生 WebSocket 的轻封装(与 Fastify 路由体系一体);Socket.IO 提供重连、房间、降级等全套方案。简单实时推送用官方插件;复杂实时协作用 Socket.IO(也能挂在 Fastify 上)。
Q:WebSocket 路由能享受 Schema 校验吗?
握手阶段的 query/headers 可以用 Fastify 的 Schema 校验;连接建立后的消息帧不走 HTTP 校验——在 message handler 里用 Ajv/Zod 自行校验消息体。
Q:如何做认证?
握手是普通 HTTP 请求:在 preValidation 钩子里校验 token(header 或 query 携带),失败直接返回 401,WebSocket 升级不会发生。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。