Skip to content

WebSocket 事件流式传输 ​

使用 GET /recordings/{recordingId}/events/ws 作为高级自定义传输方式,通过单个 WebSocket 连接流式传输 rrweb 事件。对大多数集成而言,浏览器客户端已经负责该传输、缓冲和 HTTP 回退。如果你维护自己的传输方式,请先阅读从 rrweb 迁移中的可靠性限制。

应该用 packer 还是 WebSocket 流? ​

两者工作在不同的层面,因此如何选择取决于你运行的是哪种后端。

packer 压缩单个事件,使它们无论存储在哪里都占用更少空间。它是库的功能,见优化存储。WebSocket 流是把事件实时传送到 rrweb Cloud 的传输方式,并以 HTTP 作为回退。使用自己的后端时由你决定是否打包。使用 rrweb Cloud 时,传输已经为你处理好了。

认证 WebSocket 连接 ​

浏览器 WebSocket 连接在升级期间无法设置自定义 Authorization 请求头。请改为将浏览器安全的公开写入密钥放在 token 查询参数中:

ts
const recordingId = crypto.randomUUID();
const publicWriteKey = 'public_key_rr_your_key';
const url = new URL(
  `wss://api.rrweb.com/recordings/${encodeURIComponent(recordingId)}/events/ws`,
);
url.searchParams.set('token', publicWriteKey);
url.searchParams.set('contentType', 'application/x-ndjson');

const socket = new WebSocket(url);

绝不要在浏览器代码或 WebSocket URL 中放置私有 API 密钥。URL 中的公开密钥仍然是凭据:WebSocket URL 可能出现在代理日志、监控、遥测和错误报告中。请遮盖 token 查询参数,并避免记录完整的 URL。

可选的查询参数包括:

  • contentType:application/x-ndjson、application/ndjson 或 application/json;默认为 application/x-ndjson。
  • contentEncoding:gzip、br 或 zstd;省略它表示使用 identity 编码。
  • debug=true:回显解码后的负载数据和转发详情。请仅在隐私安全的测试录制上使用它,因为这些帧包含捕获的数据。

通过 WebSocket 发送 rrweb 事件 ​

让 record() 创建事件对象。rrweb 会提供数值型事件 type、毫秒级 timestamp 和事件特定的 data;不要自行编造字符串事件类型或占位快照。

ts
import { record } from '@rrweb/record';

let stopRecording: (() => void) | undefined;
const retainedEvents: unknown[] = [];

socket.addEventListener('open', () => {
  stopRecording = record({
    blockSelector: '[data-private]',
    maskTextSelector: '[data-mask]',
    maskAllInputs: true,
    emit(event) {
      if (socket.readyState !== WebSocket.OPEN) {
        retainedEvents.push(event);
        stopRecording?.();
        return;
      }

      const frame = `${JSON.stringify(event)}\n`;
      socket.send(frame);
    },
  });
});

function finishRecording() {
  stopRecording?.();
  socket.close(1000, 'capture complete');
}

retainedEvents 仅用于演示发送失败时绝不能丢弃事件。它只存在于内存中,不是生产级的重试队列;如果页面终止或崩溃时不得丢失队列,请在捕获之前将队列及其已确认的位置持久化。

文本帧适用于未压缩的 NDJSON。仅当负载按照 contentEncoding 编码时才使用二进制帧。发送前使用 TextEncoder 测量编码后的字节数;摄取路由会拒绝达到或超过 1 MB 的单个帧。

确认事件已被接受 ​

服务器在缓冲约 256 KiB 数据后,或在约一秒无活动后,会启动一次上游刷新。关闭 socket 会请求最终刷新。最终成功的结果具有以下结构:

json
{
  "type": "upstream-result",
  "ok": true,
  "status": 204,
  "statusText": "",
  "headers": {},
  "final": true
}

例行成功的非最终刷新不会产生消息。失败、被隔离的行、调试输出和最终刷新可能产生消息。正常的最终结果以代码 1000 关闭;上游失败则以 1011 关闭。

这些结果消息不是逐事件的或应用级的确认。WebSocket.send() 只是把帧交给浏览器,断开连接可能让客户端无法判断哪些传输中的事件已被 Cloud 接收。重试不确定的帧可能使事件重复或乱序;丢弃它们则可能破坏快照链。

在投入生产之前,请设计持久队列、序列检查点、确认、恢复和重试行为。如果你的应用不包含这些保证,请使用浏览器客户端,或使用从 rrweb 迁移中的串行化 HTTP 传输。