性能优化 精选推荐

RUM 真实用户监控方案设计

HTMLPAGE 团队
22 分钟阅读

从指标体系、采样策略、上报链路到数据建模与告警,完整设计一套可落地的 RUM(Real User Monitoring)体系,解决“线上性能到底怎样”的问题。

#性能监控 #RUM #可观测性 #Core Web Vitals #采样

RUM 真实用户监控方案设计

如果说 Lighthouse/Performance 面板告诉你“理论上能跑多快”,那么 RUM(Real User Monitoring) 才能回答:

  • 真实用户到底慢在哪?
  • 慢是“某地区/某机型/某版本/某网络”的问题还是全局问题?
  • 优化上线后是否真的变好?有无回归?

这篇文章不讲“把 SDK 丢进去就完事”,而是站在体系设计角度,从 0 到 1 设计一套能长期演进的 RUM:

  • 指标体系(Core Web Vitals + 业务指标)
  • 采样与成本控制
  • 上报协议与可靠性
  • 事件模型与数据仓库建模
  • 分析维度(分桶)与聚合
  • 告警与 SLO
  • 隐私与合规

1. 先定目标:RUM 的“产品定义”

在做任何技术实现前,你必须把目标写成一句可验证的话:

我们要用 RUM 在 7 天内发现性能回归,并能定位到“哪类用户/哪条链路/哪个版本”导致 INP 恶化。

RUM 的价值通常分三层:

  1. 可见性:上线后真实体验有数据
  2. 可定位:能切片(地区/设备/页面/版本)
  3. 可闭环:告警 → 归因 → 修复 → 复盘

如果你只做第 1 层,它会退化成“报表”。


2. 指标体系:不要只盯 Core Web Vitals

2.1 必选:Core Web Vitals + 补充指标

建议最小集合:

  • LCP(最大内容绘制)
  • INP(交互到下一次绘制)
  • CLS(布局偏移)
  • TTFB(服务端首字节)
  • FCP(首屏内容绘制,辅助解释 LCP)

但这还不够,你还需要“解释型指标”:

  • Long Task(长任务)统计(数量/总时长/Top 贡献)
  • Resource timing(关键资源耗时)
  • JS 错误/资源错误(与性能回归强相关)

2.2 业务指标:把性能与转化绑定

纯性能指标很容易变成“工程师自嗨”。

建议定义 1~3 个业务级指标:

  • 首次有效交互时间(例如搜索可用、下单可点)
  • 首次关键接口完成(例如 /api/me 完成)
  • 转化漏斗关键点耗时(例如“加入购物车”到“支付成功”)

这些指标让你能回答:

  • “慢 200ms 对业务有没有影响?”

3. 采样策略:RUM 成败的 50%

RUM 的难点不是“能不能采”,而是“采多少、怎么采、怎么省钱”。

3.1 两种采样:会话采样 vs 事件采样

  • 会话采样:进入站点就决定该会话是否采集全部指标
    • 优点:链路完整、便于归因
    • 缺点:成本高
  • 事件采样:每种事件独立采样(例如性能 10%、错误 100%)
    • 优点:更灵活
    • 缺点:同一会话数据不完整

生产建议:两者结合。

3.2 分层采样建议

一个可用的默认配置:

  • 性能指标:10%(按会话)
  • 错误指标:100%(或至少 50%)
  • 关键交易链路:100%(只对“完成交易”的会话上报完整链路)

3.3 动态采样:用“预算”来管理

你可以给 RUM 设预算:

  • 每日上报不超过 N 条
  • 每秒不超过 M 条

当超预算时:

  • 降低非关键事件采样率
  • 只保留异常事件(例如 LCP > 4s)

4. 客户端采集:指标怎么拿?

4.1 Web Vitals 指标

现代浏览器提供了可观测入口:

  • PerformanceObserver
  • performance.getEntriesByType()

你可以使用社区实现(例如 web-vitals 思路),但要明确:

  • 指标口径要固定(同一页面、同一版本)
  • 不同浏览器差异要做兼容

4.2 上下文(Context)是 RUM 的灵魂

只上报 LCP=2500ms 没意义。

你至少需要这些维度:

  • 页面:path, routeName, referrer
  • 用户:匿名 userIdHash(可选)
  • 设备:deviceMemory, hardwareConcurrency, viewport
  • 网络:effectiveType, rtt, downlink
  • 版本:appVersion, buildId, commitSha
  • 环境:prod/staging, region, cdnPop

这些字段必须:

  • 控制体积(短字段名/枚举)
  • 可被服务器校验(防伪造污染)

5. 上报链路:可靠性与性能不能两头都丢

5.1 传输:优先 sendBeacon,降级到 fetch(keepalive)

  • 页面卸载时 sendBeacon 更可靠
  • 非卸载场景可用批量 fetch

5.2 批量与压缩

建议:

  • 以 5~20 条为一个 batch
  • gzip/br 压缩(服务器支持)
  • 限制 payload 大小(例如 < 64KB)

5.3 去重与重试

  • 给每条事件生成 eventId
  • 服务端按 eventId 去重
  • 客户端失败重试次数有限(例如 2 次)

5.4 采集对页面的影响

RUM 本身不能成为性能负担。

实践建议:

  • 采集计算尽量放在 idle(requestIdleCallback
  • 上报放在后台、批量
  • SDK 初始化延后(首屏后)

6. 事件模型与数据建模:决定你能不能分析

6.1 事件类型(Event Types)

建议至少定义:

  • page_view
  • web_vitals
  • resource_timing
  • long_task
  • js_error
  • api_timing(业务关键接口)

每种事件都有“必填字段”和“可选字段”。

6.2 存储建模:宽表 + 明细表

常见两种设计:

  • 宽表(便于查询):把常用字段打平
  • 明细表(便于追踪):存完整 payload(可选)

一个可落地的折中:

  • web_vitals 为核心宽表(用于大盘/SLO)
  • session_trace 为明细表(只对采样会话存)

6.3 聚合口径:p75/p90/p95/p99

RUM 的统计不要只用平均值。

推荐:

  • p75:更贴近“多数用户体验”
  • p95/p99:定位极端慢问题

对于 Web Vitals,Google 口径常用 p75。


7. 分析维度:怎么做到“可定位”?

可定位的关键在于“维度设计”。

建议固定一套常用切片:

  • 页面(路由)
  • 地区/运营商
  • 设备档位(内存/CPU 核数分桶)
  • 网络(4g/3g/slow-4g)
  • 版本(最近 10 个 buildId)
  • 入口来源(referrer/channel)

注意:维度越多,成本越高。

实践做法:

  • 维度字段做枚举/分桶
  • 对长尾维度做 Top N

8. 告警与 SLO:让 RUM 成为“系统”

8.1 SLO 的定义

示例:

  • 过去 1 天内,p75 LCP < 2500ms
  • 过去 1 天内,p75 INP < 200ms
  • 过去 1 天内,CLS p75 < 0.1

8.2 告警规则

建议用“双阈值”防抖:

  • 5 分钟窗口连续超阈值
  • 且样本量 > 最小值(例如 300)

8.3 回归检测

把版本作为维度:

  • 新版本的 p75 INP 相比上版本恶化 > 15%
  • 触发告警并附带“top 页面 / top 设备 / top 地区”

9. 隐私与合规:你必须提前做的约束

  • 禁止上报 PII(手机号、邮箱、身份证等)
  • URL 中 query/fragment 需要脱敏
  • 事件 payload 做字段白名单
  • 允许用户 opt-out(尤其是欧盟地区)

如果产品面向多地区,建议把合规当成“需求”,不是“上线前检查”。


10. 最小可用方案(MVP)建议

如果你要在 1~2 周内把 RUM 跑起来:

  1. 先采:page_view + web_vitals + js_error
  2. 先做:按路由维度的大盘(p75)
  3. 再做:版本对比(回归检测)
  4. 最后做:会话 trace(定位长尾)

总结

RUM 的关键不是 SDK,而是:

  • 指标体系与业务目标绑定
  • 采样与预算控制成本
  • 上报可靠且不影响性能
  • 数据建模支持切片与聚合
  • 告警/SLO 能闭环