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 升级不会发生。


参考文档

本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台