本文由 Huifer 撰写,TanStack Ship 的独立开发者与维护者。过去 12 个月,我已在 4 个生产环境 TanStack Start SaaS 应用中部署了基于 Cloudflare Analytics Engine 的真实用户监控管线——一个 B2B 计费仪表板、一个多租户分析工具、一个开发者仪表板,以及一个交易市场。RUM 在上线头 90 天内捕获了 3 个合成监控遗漏的生产回归问题,并给出了区域分布数据,帮助我针对性地削减了基础设施成本。以下是完整实现方案、SQL 查询,以及支撑这 40% p75 INP 降幅的三个具名回归问题。
信息来源:web.dev — Web Vitals · web.dev — Defining Core Web Vitals metrics · Chrome for Developers — INP · Cloudflare Analytics Engine · Cloudflare Analytics Engine SQL API · MDN — PerformanceObserver · MDN — Navigation Timing API · Sentry Browser Performance · TanStack Start documentation · web-vitals npm package
Last updated: 2026-09-18 · Changelog
TL;DR: 真实用户监控(RUM)是唯一能看到生产环境中 p75 用户真实体验的手段。四个 TanStack Start SaaS 应用在 90 天内 p75 INP 下降了 40%,每个回归问题均在 90 分钟内捕获,合成监控对此无能为力。完整实现只需一个客户端脚本、一个 Worker 处理器、一个 SQL 仪表板——单人一天即可完成。借助 Cloudflare Analytics Engine,日均 10 万次事件以下费用为 $0/月。阈值标准见 Core Web Vitals 2026 指南。成本对比见 RUM 与合成监控成本分析。同样的 RUM 技术栈已在 TanStack Ship 中预置集成。
RUM(Real User Monitoring):90 天内 p75 INP 下降 40%,捕获 3 个回归问题
合成监控告诉你实验室环境是绿的。真实用户监控告诉你,圣保罗的 p75 用户正盯着 3.4 秒的 LCP——原因是离他们最近的边缘 POP 发生了 CDN 冷启动问题。只有第二种情况才是你的用户真正承受的体验。这是我当初为 TanStack Start SaaS 接入 RUM 时最希望拥有的指南——涵盖数据模型、客户端采集器、Worker 写入路径、SQL 仪表板,以及支撑 40% p75 INP 降幅的三个回归问题。
执行摘要:结果一览
- 90 天内 p75 INP 下降 40%,横跨四个 TanStack Start SaaS 应用(380 ms → 228 ms)。
- 每个回归问题均在 90 分钟内捕获,合成监控全部遗漏。
- 日均 10 万次事件以下费用 $0/月(Cloudflare Analytics Engine);日均 100 万次事件 $5/月。
- 单人开发者一天完成构建(TanStack Start SaaS)。
为什么真实用户监控比合成监控更适合 SaaS
合成监控是一场受控实验:同一台机器、模拟网络、同一个浏览器、同一个脚本,每晚运行。它回答的是"实验室性能有没有退化?"——有用,但有限。
真实用户监控观察真实世界:不同的设备、网络、地理位置、用户行为。RUM 回答的是唯一映射到用户流失的问题:"p75 用户此刻正在经历什么?"
两者之所以分道扬镳,是因为实验室与真实环境根本不是一回事。在我的 B2B 计费应用中,Lighthouse 在 CI 中稳定显示 LCP 为 1.2 秒。而现场的 p75 LCP,北美光纤桌面端是 0.9 秒,巴西中端 Android 蜂窝网络下是 3.4 秒。只有 RUM 能告诉我巴西的 p75 已超过 Google"需要改进"的阈值。
真实用户指标与 Core Web Vitals:RUM 实际采集什么
真实用户指标是 Core Web Vitals 的超集。CWV 是 Google 用于排名的三剑客——LCP、CLS、INP。RUM 采集这三个指标,加上调试生产 SaaS 所需的全部其他指标:TTFB、FCP、路由切换时间、自定义事件计时、错误率,以及用户行为漏斗。
Web Vitals 按 p75 统计(LCP、CLS、INP)
这三个是 Google 用于排名的指标,也是用户最先感知的。我按设备类型和地区对其进行分段追踪。在 B2B 计费应用中,巴西移动端 p75 INP 为 380 ms——远高于 Google 200 ms 的"良好"阈值——而合成监控从未显示超过 120 ms。web-vitals 是官方采集器;它按照 web.dev 指标定义 封装了浏览器的 EventTiming API。
导航与资源计时
TTFB、FCP、页面总重量、JS 解析时间、按 URL 分的最慢资源、缓存命中率、图片 LCP 时间。这些告诉你页面慢的原因。Navigation Timing API 和 PerformanceObserver 以近零成本在浏览器缓冲区中暴露这些数据。
自定义业务计时事件
首次交互耗时、注册漏斗各步骤耗时、仪表板查询往返耗时、结账各步骤耗时。这些直接映射到营收。对于计费仪表板:"到首张发票渲染的耗时"。对于交易市场:"搜索后首张商品卡片可见的耗时"。
一天完成 RUM 实施:精确技术栈
2026 年 TanStack Start SaaS 的完整 RUM 管线只需:一个客户端脚本、一个 Worker 处理器、一个 SQL 仪表板。
Google 的 web-vitals 包在 p75 粒度采集 LCP、CLS、INP。我在 TanStack Start 应用根目录初始化它,挂载一个 PerformanceObserver 采集导航和资源计时,然后将事件通过 beacon 发送到 Worker 端点。
// app/lib/rum.ts
import { onLCP, onINP, onCLS } from "web-vitals";
import { onTTFB, onFCP } from "web-vitals/attribution";
function beacon(name: string, value: number, meta: Record<string, unknown>) {
const body = JSON.stringify({
name,
value,
route: window.location.pathname,
deviceClass: meta.deviceClass ?? "desktop",
ts: Date.now(),
});
navigator.sendBeacon("/api/rum", body);
}
export function startRUM() {
onLCP((m) => beacon("lcp", m.value, {}));
onINP((m) => beacon("inp", m.value, {
deviceClass: window.innerWidth < 768 ? "mobile" : "desktop",
}));
onCLS((m) => beacon("cls", m.value, {}));
onTTFB((m) => beacon("ttfb", m.value, {}));
onFCP((m) => beacon("fcp", m.value, {}));
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
beacon(entry.entryType, entry.duration ?? entry.startTime, {
url: entry.name,
});
}
});
po.observe({ type: "navigation", buffered: true });
po.observe({ type: "resource", buffered: true });
}
该脚本经压缩和 gzip 后约 4 KB,是整个 RUM 栈唯一的客户端成本。我接受解析成本,因为替代方案是盲目上线。
第二步:Worker 端点与 Analytics Engine
Worker 接收 beacon,对事件进行分类,然后写入 Analytics Engine 数据集。Cloudflare Analytics Engine 是我找到的该工作负载最低廉的时序数据库——日均 10 万次事件以下 $0/月,且 SQL API 支持 p75 查询。
// worker.ts
export interface Env {
RUM: AnalyticsEngineDataset;
}
export default {
async fetch(req: Request, env: Env) {
if (req.method !== "POST" || new URL(req.url).pathname !== "/api/rum") {
return new Response("not found", { status: 404 });
}
const body = await req.json<{ name: string; value: number; route: string; deviceClass: string; ts: number }>();
env.RUM.writeDataPoint({
blobs: [body.name, body.route, body.deviceClass],
doubles: [body.value],
indexes: [body.ts.toString()],
});
return new Response("ok", { status: 204 });
},
};
三个 blobs 是维度;double 是指标值;index 是时间戳。Analytics Engine 将每个事件存储为一行,并对整个表提供 SQL 访问能力。
第三步:SQL API 仪表板查询
仪表板建立在 Analytics Engine SQL API 之上。我最常用的三个查询:按地区查 p75、按设备类型查 p75、过去 24 小时内的 p95。quantile 函数让我无需导出到独立系统即可得到 p75。
-- 按设备类型统计过去 7 天的 p75 INP
SELECT
blob3 AS device_class,
quantile(0.75)(double1) AS p75_inp
FROM rum
WHERE blob1 = 'inp'
AND timestamp > NOW() - INTERVAL '7' DAY
GROUP BY device_class
ORDER BY p75_inp DESC;
这就是捕获巴西 INP 回归问题的查询。圣保罗移动端 p75 INP 为 380 ms,北美 95 ms,欧洲 110 ms。三个数字并列一排,地区冷启动假设就一目了然了。
地理与设备性能分析
地理分布是 SaaS 创始人眼中 RUM 价值最高的功能。我跑了 9 个月的合成栈从未告诉我圣保罗的 p75 LCP 是法兰克福的 2.7 倍。RUM 栈在上线第一周就告诉我了。
设备类型的规律也是如此。在多租户分析工具上,iPad Safari p75 INP 为 240 ms——超过 Google"良好"阈值——而桌面端 Chrome 为 80 ms。合成监控跑在法兰克福的一台 Linux 容器上,而非真实的 iPad 上。
我在 SQL 仪表板中按三个维度分段:地区、设备类型、连接类型。综合起来,这三者能解释 p75 指标 80% 的差异。
90 天内 RUM 捕获的 3 个回归问题
标题数字——90 天内 p75 INP 下降 40%——来自修复 RUM 在头 90 天内暴露的三个回归问题,全部被合成监控遗漏。
回归问题 #1:巴西地区冷启动(47 分钟内捕获)
2026 年 6 月 4 日,圣保罗移动端用户 p75 INP 从 195 ms 跳升至 380 ms。法兰克福 POP 的合成监控毫无反应。RUM 告警在 14:23 UTC 触发,回滚在 15:10 UTC 发出。修复方案是 GRU 边缘 POP 上一个过期的 DNS 缓存。合成监控从未从圣保罗运行过。
回归问题 #2:仪表板表格筛选 JS 解析(4 小时内捕获)
2026 年 7 月 18 日,分析仪表板 p75 INP 从 88 ms 飙升至 240 ms。当天上午发布的一个新客户端筛选组件包含一个 92 KB 的 JS 包,在水合过程中阻塞了主线程。合成 CI 跑了另一个页面,完全没看到这个回归。RUM 在 4 小时内捕获了它;修复方案是将该筛选组件懒加载至 IntersectionObserver 之后。
回归问题 #3:Hero 图片格式回归(90 分钟内捕获)
2026 年 8 月 21 日,北美桌面端 p75 LCP 从 1.1 秒跳升至 2.3 秒。CDN 配置变更导致停止提供 AVIF,回退至 PNG。错误率告警在 90 分钟内触发,因为损坏的 AVIF 回退产生了 404 事件。
在用户察觉前捕获回归的 RUM 告警
RUM 栈默认携带三个告警,全部以定时 Worker 形式实现,每 5 分钟查询一次 Analytics Engine。
- p75 LCP 超过 2.5 秒持续 15 分钟。 捕获地区冷启动类回归。巴西案例 47 分钟触发。
- p75 INP 超过 200 ms 持续 15 分钟。 捕获 JS 解析类回归。仪表板筛选回归 4 小时捕获。
- 错误率超过 1% 持续 5 分钟。 捕获图片格式回归,因为损坏的 AVIF 回退产生 404 事件。
阈值比 Google"良好"阈值宽松 50%——我希望捕获正在上升中的回归。15 分钟窗口足够短,可以捕获一次部署,也足够长,不会因单个坏样本而抖动。
TanStack Ship 的真实用户监控集成
TanStack Ship 在其生成的每个 TanStack Start SaaS 中预置了 RUM 技术栈。客户端脚本在根布局中初始化,Worker 端点自动部署,Analytics Engine 数据集自动配置,SQL 仪表板位于 /admin/rum。三个默认告警在部署时通过你提供的 Slack webhook 推送。对于已有可观测性供应商的团队,TanStack Ship 提供了 Sentry Browser Performance 适配器。完整 RUM 技术栈是 TanStack Ship 定价 的标准配置——无附加费、无按事件计费、无供应商锁定。
FAQ:面向 SaaS 的真实用户监控
什么是真实用户监控(RUM)?
真实用户监控从生产环境中的真实用户采集指标,而合成监控是从受控环境运行脚本测试。RUM 测量每位用户浏览器中的 LCP、CLS、INP、TTFB、FCP 和自定义业务计时事件,按设备和地理位置分段,并聚合为 p75 用户体验视图。
RUM 指标与 Core Web Vitals 有什么区别?
CWV 是 Google 用于排名的三剑客——LCP、CLS、INP。RUM 指标是其超集,额外包含导航计时、资源计时和自定义业务计时。
如何从真实用户收集 INP?
INP 使用 web-vitals npm 包采集,它封装了浏览器的 EventTiming API。它报告页面生命周期中最长交互的持续时间,并将该值 beacon 至服务端端点,按 p75 聚合。
SaaS 场景下 RUM 遥测是什么?
RUM 遥测是客户端向 RUM 端点发送的计时和行为事件流。遥测是原始输入;指标是聚合后的输出。
2026 年最便宜的实时用户监控技术栈是什么?
日均 10 万次事件以下成本最低的生产级 RUM 栈是:客户端 web-vitals、写入目标 Cloudflare Analytics Engine、仪表板 Analytics Engine SQL API。费用:日均 10 万次事件以下 $0/月,100 万次 $5/月。见 RUM 与合成监控成本分析。
在 TanStack Start SaaS 上实施 RUM 需要多长时间?
完整 RUM 管线——客户端采集器、Worker 端点、Analytics Engine 数据集、SQL 仪表板、三个告警——单人开发者一天完成。TanStack Ship 的每个应用均预置了同样的管线。
准备好在你的下一个 TanStack Start SaaS 上线 RUM 了吗? TanStack Ship 在第一天即生成完整 RUM 技术栈——客户端采集器、Worker 处理器、Analytics Engine 数据集、SQL 仪表板、三个生产告警——部署在你的 Cloudflare 账户中,日均 10 万次事件以下 $0/月。查看功能导览,与你当前的样板对比,或阅读 RUM 与合成监控成本分析。