{T}

前端监控体系与性能平台

对于线上应用,除了前期的开发和设计,上线后的维护同样重要,其中就包括监控体系的搭建。搭建前端监控体系主要是为了解决两个问题:如何及时发现问题?如何快速定位并解决问题?

一个完整的前端监控体系包含:数据采集(埋点与收集)→ 性能 SDK → 数据上报 → 性能平台(数据处理 + 可视化)→ 监控预警 → 问题诊断 → 效果评估。

一、监控什么数据

监控数据从五个维度采集,覆盖页面访问速度、稳定性、外部服务调用等情况:

数据类别关注点采集方式
生命周期数据页面性能、访问情况PerformanceTiming、框架生命周期
HTTP 测速数据外部服务调用、性能优化PerformanceTiming
系统异常数据系统稳定性、异常问题window.onerrorerror 事件
用户行为数据页面稳定性、访问情况DOM 事件监听
用户日志问题排查埋点输出

1. 生命周期数据

前端应用的生命周期指页面加载的关键时间点,可通过 PerformanceTiming 获取:

javascript
// 页面加载关键时间点
const timing = performance.timing;
navigationStart;                    // 页面跳转
domLoading;                         // DOM 开始解析
domInteractive;                     // DOM 解析完成
domContentLoadedEventEnd;           // DOMContentLoaded 完成
loadEventEnd;                       // 页面加载完成

但框架化后,DOMContentLoadedreadystatechange 失去原本作用(页面渲染由框架控制),更多在框架生命周期钩子中收集(如 Vue 的 mounted、React 的 componentDidMount)。也可以结合 MutationObserver 监听 DOM 变化:

javascript
const observer = new MutationObserver((mutations) => {
  console.log(`时间:${performance.now()},DOM 树变化类型:`);
  mutations.forEach((m) => console.log(m.type));
});
observer.observe(document, { childList: true, subtree: true });

2. 系统异常数据

常见前端异常包括:逻辑错误、代码健壮性问题、网络错误、系统错误(运行环境兼容性)、页面内容异常。前四类可通过 window.onerrorerror 事件、XMLHttpRequest.status 等拦截上报:

javascript
window.addEventListener('error', (event) => {
  // 收集错误信息并上报
  reportError({
    message: event.message,
    stack: event.error?.stack,
    source: event.filename,
    line: event.lineno,
    column: event.colno,
  });
});

window.addEventListener('unhandledrejection', (event) => {
  reportError({ type: 'unhandledrejection', reason: event.reason });
});

页面内容异常一般通过回归测试、UI 测试在上线前避免,或通过观察用户操作数据、页面访问数据异常来辅助判断。

3. 用户行为数据

包括页面浏览量/点击量、用户停留时间、访问入口、页面操作行为。通过 DOM 事件监听、页面加载情况获取。用户行为数据既能监控页面功能是否正常(功能异常必然导致点击量变化),也能分析用户操作链路。

4. 用户日志

异常定位依赖日志。可通过装饰器、方法劫持等方式自动打印输入参数、执行信息和输出参数。日志存放方式:上报服务器(成本高、可能丢失)或本地存储(依赖用户提交),可两者配合。

二、数据埋点

数据埋点有三种主流方案:

方案特点优点缺点
代码埋点手动在业务代码中埋点灵活、准确接入和维护成本高
可视化埋点通过可视化界面配置成本较低灵活性中等
无痕埋点固定 SDK 自动采集成本低、接入快自定义能力弱

实际使用中常将多种方案配合使用,如用无痕埋点采集基础数据,用代码埋点补充个性化需求。

无论哪种方式,都需要对数据进行标准化处理(按与服务端约定的协议格式转换),并配合本地缓存避免用户关闭应用、浏览器异常导致数据丢失。

三、性能 SDK 设计

性能 SDK 将采集代码和上报策略封装在一起,为公司各业务提供统一的性能统计能力。设计需满足两个要求:接入简单易用、运行平稳高效

1. API 设计

将首屏、白屏、卡顿采集封装为统一 API:

  • FMP API:首屏采集(calculateScore 分数计算、calFinallScore 变化率计算、fmpImg 首屏图片时间,后者独立抽为 util)
  • FP API:白屏采集
  • BLOCK API:卡顿采集
  • Extension API:扩展数据(如加载瀑布流,将首屏细分为 DNS、TCP 等时间)

最终封装为 Perf API 统一调用,先做环境兼容性判断(是否支持 window.performance)。

接入方式:

javascript
// npm 包方式
npm install @common/Perf -S;
import { perfInit } from '@common';
perfInit();

// 外链方式
// <script src="https://s1.static.com/common/perf/static/js/1.0.0/perf.min.js"></script>
try {
  perfInit();
} catch (err) {
  console.warn(err);
}

提供帮助文档(接入示例、QA、调试参数 PERF_DEV_MODEL)可提升易用性,让工程师接入时能快速定位初始化失败、采集异常、请求参数错误等问题。

2. 运行设计

  • 兼容性策略:用原生 JavaScript 做指标采集,实现跨技术栈(React/Vue)复用;不同终端(PC/移动/小程序)通过适配层抹平差异(如小程序用 minaFMP,其他端用 FMP)。
  • 容错机制:SDK 自身报错用 try/catch 捕获并上报异常平台,且不能因 SDK 报错影响页面运行。
  • 自测与 QA:按用户 top10% 机型和浏览器类型自测;升级时无论功能大小都做冒烟测试。

四、上报策略

1. 日志数据过滤

上报前过滤两类异常数据:计算错误的异常数据(负值、非数值)和合法但异常的极大/极小值(如 15s 以上的首屏时间)。负值会严重拖低首屏时间;字符串类型数值(如 "200")在计算时会出现 "200"+30=20030 的错误。过滤到的异常数据记录日志并上报异常平台。

2. 数据抽样

根据日活决定是否抽样:日活 10 万以下无需抽样;千万级日活必须抽样(如 58 同城做 10% 抽样)。业界也通过服务端下发抽样率动态控制,如双十二日活激增时抽样率从 10% 降到 5%,降低统计服务器负载。

3. 上报机制

  • 按网络能力:强网(4G/WIFI)直接上报,弱网(2G/3G)本地存储、延时到强网再上报。
  • 按 App 状态:空闲时上报,忙碌时等闲时(如凌晨 2-3 点)上报。
  • 批量上报:默认消息达到 30 条才上报,或只在 App 启动时上报。
  • 接口选择:建议优先使用 native 接口上报,可复用客户端请求连接,支持延时/批量上报。

4. 上报时机(前端通用)

时机说明
定期/定量上报本地缓存,达到一定数量或固定时间间隔打包上传
关键生命周期上报异常触发时、用户退出程序前上传
用户主动提交引导用户主动上传本地数据和日志

五、性能平台架构

性能平台是 Web 系统,包含后台数据处理前台可视化展示两部分。

1. 技术架构

code
数据接入层 → 数据计算层 → 存储层 → 平台层
(Node.js)    (Kafka/Spark/  (MySQL/    (React/AntV/
             Hive/HDFS)      MongoDB)    Less)
  • 数据接入层:接收 SDK 上报数据,做协议转换后作为生产者写入 Kafka。
  • 数据计算层:作为消费者从 Kafka 读数据存 Hive,用 Spark 做数据计算。
  • 存储层:MySQL(账号权限、关注业务模块表)+ MongoDB(单条性能数据)。
  • 平台层:React + Ant Design + AntV 可视化,nginx 做 Web Server,compression 做 Gzip。

2. 数据处理流程

第一步,入库:SDK 上报数据经后端(Node.js Controller)处理,URL 解析成 key-value,删除空数据、舍弃异常数据,写入 Kafka。使用 Kafka 而非直接入库 Hive,是为了应对高并发流量洪峰和避免数据重复(消息队列消费后删除)。

第二步,数据清洗与计算:用 Spark 处理重复数据(去重)、缺失数据(按 Performance 数据补全,无法补全则舍弃)、错误数据(负值、超 10s 则舍弃)。然后计算首屏时间分布、秒开率(首屏 ≤1s 占比)、页面瀑布流时间(DNS/TCP/请求耗时/DOM 解析等细分)。

第三步,准备可视化数据:用 Node-schedule 定时从 Spark 取数据导入 MongoDB。

3. 可视化前台

  • 大盘页:展示各业务性能简图(首屏时间、秒开率、采样 PV)。
  • 详情页:补充秒开率、性能均值、白屏均值细节,以及终端分布(iOS/Android 比例),展示页面加载瀑布流帮助定位瓶颈。

六、监控预警

监控预警要求实时,因此数据清洗后一个分支用 Spark 计算,另一个分支用 Flink 实时计算。

预警流程

  1. 超过阈值的数据(如首屏 >2s、判定为卡顿)标记为预警数据。
  2. 用 Node-schedule 定时任务将预警数据拉取到 MongoDB 预警表。
  3. 按严重程度分级报警:企业微信报警 → 邮件报警(node-mailer)→ 短信报警。

以手机列表页为例:性能标准是首屏 1.5s、秒开率 90%。超出 10% 标红并企业微信报警,超出 20% 邮件报警,超出 30% 短信报警。

注意:预警通知消耗通信资源,为避免浪费,一般只对 App 首页核心导航位等关键页面做监控预警。

灰度监控:上报数据时带上版本号,灰度过程中关注——错误告警是否有新增、全版本功能点覆盖曲线是否正常、分版本新旧版本转化率是否一致。可关联 BUG 管理系统自动生成 BUG 单。

七、问题诊断

发现性能问题后,先确认是共性问题还是个例问题

  • 共性问题:大量用户数据指标异常(如某网络下首屏均值超 3s)。通过性能平台的加载瀑布流定位具体瓶颈点(如某 JS 文件被阻塞)。
  • 个例问题:偶发性因素(个人网络抖动、手机内存占用多、用户连代理),不需要专门优化,一般客服联系用户解决。

诊断清单(四个角度):

角度说明示例
全量 vs 增量数据是否一次拉全量列表页首屏拉 4 条,滚动再加载后续
同步 vs 异步接口是否同步阻塞并行请求导航和商品两个接口
实时 vs 缓存数据是否必须实时榜单/静态资源走缓存,价格/库存走实时
原片 vs 压缩图片是否用原图用 webp,或用低清图占位再优化

八、效果评估

性能优化的最终目标是提升业务数据指标。数据显示:沃尔玛在线网站页面加载时间每减少 1 秒,转化率增加 2%(前提是首屏时间远大于 1s,若已秒开提升有限)。

评估方式:AB 测试。将页面分为 A/B 两个版本(通过条件语句、模板或路由区分),对比优化前后的转化率:

javascript
// 通过 URL 参数区分版本
if (params.version === 'A') {
  // 优化前的逻辑
} else {
  // 优化后的逻辑
}
// 下单时带上 from=version 标记,统计埋点对比转化率

AA 测试:在 AB 测试前,先做两个版本代码完全一致的 AA 测试,确保版本差异带来的波动率在万分之一以下,保证 AB 测试结果可信。

注意事项:性能优化项目一定要做好兼容性测试,特别是 Top 10 机型和弱网环境,避免出现兼容性问题导致转化率反而下降。

九、总结

  • 前端监控体系解决「如何及时发现、如何快速定位」两个问题,形成"采集 → 上报 → 监控 → 预警 → 诊断 → 评估"的闭环。
  • 性能 SDK 是监控体系的关键,设计要保证接入简单、运行平稳(兼容性、容错、自测)。
  • 性能平台提供数据处理(Kafka/Spark/Hive)和可视化(大盘/详情页),为监控预警和问题诊断提供支撑。
  • 最终用业务指标(如转化率)评估性能优化效果,通过 AB 测试验证。