做前端和运维这些年,被问得最多的一类问题就是“我们网站到底慢不慢”。这个问题听着简单,答起来却麻烦——你得先定义什么叫慢,再决定用什么尺子去量,最后还要能说清楚慢在链路、服务端、还是浏览器里那几百毫秒的渲染。网站访问速度测试工具这个领域,工具数量多到能列一长串,但真正能跑通“发现—定位—验证”闭环的其实不多。TTFB 是链路和服务端的分界线,RUM 是实验室数据和真实体验之间的校准器,这两个东西没搞清楚,测出来的分数再漂亮也只是自嗨。这篇内容面向三类人:刚接手性能优化不知道从哪下手的开发、要给团队定性能预算的技术负责人、以及被“网站打开慢”这种模糊反馈折磨过的运维。我会把 13 个常见的测速工具按使用场景拆开,讲清楚每个工具解决什么问题、在什么情况下会骗你、以及怎么把它们组合成一条能复现的流水线。
1. 先想清楚要量什么:三层视角决定工具选型
1.1 一次“玄学卡顿”引出的定位难题
几年前我接手过一个内部管理系统,用户反馈“点开列表要等三秒”。我先用浏览器开发者工具跑了一遍,本地加载 800 毫秒,各项指标绿色。换一台离机房远的机器,还是 800 多毫秒。问题在于,用户说的“等三秒”是从点击链接开始算到看见数据行,而我的测量是从导航开始到 load 事件结束。两边的起点终点都不一样,自然对不上。
这个事让我形成了一个习惯:动手测之前先把“慢”翻译成可测量的时间段。常见的翻译方式有三种。第一种是从输入网址到首屏出现内容,对应的是首字节时间加渲染时间,关注的是“有没有反应”。第二种是从点击到新页面数据渲染完毕,这是单页应用里最常见的体验痛点,涉及路由切换、接口请求、组件渲染三段。第三种是页面加载完成后用户滚动、点击时的响应流畅度,指标上对应交互延迟。这三种场景用的工具完全不同,混着用就会得出互相矛盾的结论。
所以我给团队定的第一条规矩是:任何一次测速,先在文档里写清楚测的是哪一段、在什么设备什么网络下测、样本量多少。这三行字不写,后面所有数据都不作数。这也是为什么市面上工具那么多,你却总觉得“测了跟没测一样”——不是工具不行,是问题没定义清楚。
1.2 合成监测与真实用户监测,两条腿都不能少
合成监测说白了就是雇一个机器人,在受控的环境里反复访问你的页面,记录每次的表现。它的优势是变量可控:网络条件可以模拟成 4G 或者更差的链路,CPU 可以降速四倍模拟中低端手机,缓存可以强制清空保证冷启动。缺点是它测的是“机器人体验”,不是“你的用户体验”。你的用户可能在地铁里用着三年前的中端机,后台还挂着十几个应用,这种场景合成监测永远模拟不出来。
真实用户监测(RUM)反过来,它是把一段采集脚本埋进页面,等真实用户访问时把指标回传。它拿到的是泥沙俱下的真实分布,能告诉你第七十五分位的用户有多慢、哪个机型的崩溃率最高、哪个地区的首字节时间异常。但 RUM 的问题是没有对照组——你不知道这个数据变差是因为你改坏了代码,还是因为今天进来的用户网络本来就更差。
我的做法是两条腿走路:合成监测负责“守门”,每次发布前跑一轮,指标劣化就卡住;RUM 负责“看真相”,按周看趋势,找出合成监测覆盖不到的边缘场景。合成数据是理想值,RUM 数据是现实值,两者的差距本身就是最有价值的信息——差距大说明你的用户环境和实验室环境差异大,优化方向应该往兼容性和弱网适配走。
1.3 指标口径不一致,比不测更危险
团队协作里最容易出事的地方不是没人测,而是每个人测的口径不一样。A 用平均值汇报“首页 1.2 秒”,B 用第七十五分位汇报“首页 2.8 秒”,开会时两个人吵得面红耳赤,其实说的是同一份数据。
我踩过最典型的一个坑是首字节时间的口径。有的工具算的是从发起请求到收到第一个字节,包含 DNS 解析;有的工具在连接复用的前提下只算服务端处理时间;还有的工具会把重定向的耗时也算进去。同一个页面,三种口径能差出两百毫秒。后来我在团队里统一了一份指标定义文档,明确规定:DNS 解析单独记录、连接建立单独记录、首字节时间从连接就绪开始算、重定向链路单独打点。文档不长,但省了无数次扯皮。
注意:给指标定口径这件事,一定要在项目开始时就做,不要等数据出来再补。事后统一口径意味着历史数据全部作废,趋势图直接断掉。
2. 13 个工具的定位拆解与选型逻辑
2.1 浏览器内嵌工具:从开发者面板开始
排在第一位的一定是浏览器自带的开发者工具。它的网络面板能给出每一个请求的完整瀑布图,包含排队、DNS、连接、等待、下载五段耗时;性能面板能录下主线程的活动,看清是哪段脚本阻塞了渲染。这个工具最大的价值是“零成本上手”和“颗粒度最细”,你不需要装任何东西,按 F12 就能看到问题出在哪个请求上。
但它的局限也很明显。一是只能测单次,手动操作无法批量;二是默认不清缓存,第二次刷新看到的是缓存命中后的美好数据;三是它不给你打分,也不告诉你什么算好什么算差。我通常把它当作“深挖工具”而不是“评估工具”——先用别的工具发现问题页面,再用它钻进去看细节。
用的时候有几个细节值得注意。网络面板里要勾选“停用缓存”,否则你测的是二次访问。节流选项里的“快速 3G”之类的预设其实是人为构造的极端条件,别拿它的结果当真实移动端体验。还有,性能面板录制时会把浏览器的扩展脚本也算进去,测量生产环境问题时建议开无痕窗口,把干扰降到最低。
2.2 Lighthouse:可编程的体检报告
Lighthouse 是这套工具链里我最常用的一环,原因是它能跑在命令行里,能接进流水线。它对一个页面做一轮完整审计,输出性能、可访问性、最佳实践、SEO 四个维度的分数,性能部分还会给出具体的优化建议,比如“图片未设置尺寸”“存在阻塞渲染的资源”,并且标明每一条能省多少毫秒。
命令行跑一轮的基本写法是这样:
npm install -g lighthouse lighthouse https://example.com \ --preset=desktop \ --only-categories=performance \ --output=json \ --output-path=./lh-report.json \ --chrome-flags="--headless --no-sandbox"参数上有几个值得展开讲。--preset=desktop和默认的移动端模式差别很大,移动端模式会施加四倍 CPU 降速和慢速网络模拟,同一个页面分数可能差出三十分,团队内部必须约定用哪个口径,否则数据没法横向比。--only-categories=performance能省掉无关审计,跑一轮从二十秒降到八秒左右,接在 CI 里更合适。--chrome-flags里的无头模式是必须的,不然跑在构建机上会因为没有图形界面直接失败。
Lighthouse 的分数是个加权计算结果,首字节时间、首次内容绘制、最大内容绘制、总阻塞时间、累积布局偏移这几项按不同权重折算成百分制。这里有个容易误读的点:分数高不等于体验好。我见过一个页面拿了 95 分,但那是首屏只有一张图和一个标题的落地页,真正的业务页面在登录后才加载,完全没被测量到。所以 Lighthouse 适合做“发布前的守门员”,不适合作为体验好坏的唯一依据。
2.3 在线评测服务:快速横评与竞品对照
第二类工具是在线评测平台,输入网址就能出报告,不需要本地环境。这类工具的好处是省事,坏处是你不知道它背后的测试节点在哪、网络条件怎么配的、跑了多少次取的值。我一般用它做两件事:一是快速判断一个陌生站点的大致水平,二是做竞品横向对照,看看同类产品在相同测试条件下谁更快。
用这类服务有个经验:不要只看分数,要看它给出的“机会清单”。多数平台会把可优化项按预计收益排序,排在前面的通常是图片压缩、文本压缩、缓存策略这几类,这些都是能立刻动手改的。另外要注意测试节点的地理位置,同一个站点从不同区域的节点测,结果可能差一倍以上,报告里如果没标节点位置,这个数据就没什么参考价值。
这类平台普遍有免费额度和付费额度的差别,免费版通常限制单次测试、不给历史趋势、不给详细瀑布图。如果你的需求只是偶尔看一眼,免费版够用;如果要做持续监控,就得考虑自建或者用带 API 的方案。
2.4 WebPageTest:变量控制最狠的实验室
如果要在这 13 个工具里选一个“最专业”的,我会选 WebPageTest。它能控制的变量细到令人发指:可以选择浏览器型号和版本、可以选择地理位置和网络类型、可以设置连接冷热状态、可以指定重复测试次数、可以录制视频逐帧对比首屏出现时间。它还支持“先访问一个页面再测目标页面”的脚本模式,用来测登录后的页面。
它的核心价值在于可复现。同一个配置跑十次,你能拿到中位数、标准差、每次的瀑布图,这在排查“偶发卡顿”时特别有用——单次测试看不出来的抖动,在多次测试的分布里会暴露得很清楚。
脚本模式是它的杀手锏,用来模拟真实用户路径:
navigate https://example.com/login setValue id=user demo_user setValue id=pass demo_pass clickAndWait id=submit navigate https://example.com/dashboard这段脚本的含义是先打开登录页、填入账号密码、点击提交并等待跳转、再打开目标页面开始测量。这样测出来的才是登录后的真实性能,而不是被重定向到登录页的那一秒钟。很多团队测速时忽略了这一层,测的其实是未登录状态下的空壳页面,数据好看但没意义。
2.5 持续监控类平台:把测速变成一条曲线
单次测试只能告诉你“现在怎么样”,持续监控才能告诉你“趋势往哪走”。这类平台的做法是按固定频率(比如每十五分钟或每小时)从多个节点跑一轮测试,把结果存成时间序列,指标超过阈值就告警。它们的价值不在于某一次的数据精度,而在于能捕捉到“昨天还好今天突然变慢”这种突变,并且能关联到具体的发布记录。
用这类平台最大的坑是告警疲劳。一开始把阈值设得太严,一天收到几十条告警,两周之后所有人都不看了。我的做法是分两级:一级是硬阈值,比如首字节时间超过八百毫秒连续三个采样点,这个必须处理;二级是趋势告警,比如周环比劣化超过百分之二十,这个只需要在周会上看一眼。分级之后告警才有意义。
另一个要注意的是监控频率和成本的平衡。高频监控能更早发现问题,但请求量大、费用高,而且对小站点来说频繁的测试请求本身会污染自己的访问数据。我的经验是关键页面十五分钟一次、次要页面一小时一次,足够了。
2.6 前端埋点库与自建 RUM
RUM 这一侧最轻量的做法是用现成的前端指标采集库,它封装了浏览器原生性能接口,能直接拿到首字节时间、最大内容绘制、交互延迟、布局偏移这几个核心指标,并且处理了页面可见性变化、后退缓存恢复这些边界情况。基础接入大概是这样:
import { onLCP, onINP, onCLS, onTTFB } from 'web-vitals'; function report(metric) { const body = JSON.stringify({ name: metric.name, value: metric.value, rating: metric.rating, id: metric.id, page: location.pathname, conn: navigator.connection ? navigator.connection.effectiveType : 'unknown' }); if (navigator.sendBeacon) { navigator.sendBeacon('/api/vitals', body); } else { fetch('/api/vitals', { body, method: 'POST', keepalive: true }); } } onLCP(report); onINP(report); onCLS(report); onTTFB(report);这里有个细节必须说清楚:用sendBeacon而不是普通的fetch,是因为用户在页面加载完就关掉标签页的情况下,普通请求会被浏览器直接取消,数据就丢了。sendBeacon交给浏览器在后台发送,成功率明显更高。加keepalive: true是同样的道理。
采集端只负责上报,真正的价值在服务端怎么聚合。至少要做三件事:按页面路径分组、按设备类型和网络类型分组、算分位数而不是平均值。平均值会被少数极慢的样本拉偏,分位数才能反映“大多数人的体验”。我通常看第七十五分位和第九十五分位两个数,前者代表主体用户,后者代表最差的那批人。
2.7 协议层工具:curl 与 httpstat
有时候你需要绕开浏览器,直接看网络层的表现。curl的-w参数可以输出各阶段耗时,这是最原始也最可靠的办法:
curl -o /dev/null -s \ -w "dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n" \ https://example.com输出里time_namelookup是 DNS 解析耗时,time_connect是 TCP 握手完成时间,time_appconnect是 TLS 握手完成时间,time_starttransfer是收到第一个字节的时间,time_total是整体耗时。这四个数的差值能直接定位瓶颈在哪一层:如果connect减namelookup很大,说明网络往返延迟高;如果ttfb减appconnect很大,说明服务端处理慢或者有排队。
更直观的版本是 httpstat 这类封装工具,它把上面的数字按阶段可视化,用颜色区分每一段的耗时占比。排查“到底是网络问题还是服务端问题”时,这一张图比任何评分都管用。
提示:用 curl 测速时一定要带上
-o /dev/null丢弃响应体,否则终端输出本身会拖慢测量。另外多次执行时注意连接复用,如果服务端支持长连接,第二次的time_connect会接近零,这时看到的是复用后的数据,不是冷启动数据。
2.8 13 个工具的选型对照
| 序号 | 工具 | 类型 | 最适合的场景 | 主要坑点 |
|---|---|---|---|---|
| 1 | 浏览器开发者工具 | 内嵌 | 单页面深挖、瀑布图定位 | 默认不清缓存、不给评分 |
| 2 | Lighthouse CLI | 合成 | 发布前守门、CI 集成 | 移动端与桌面端分数差异大 |
| 3 | PageSpeed Insights | 在线 | 快速体检、拿优化建议 | 混合了实验室与真实数据 |
| 4 | WebPageTest | 合成 | 变量严控、多轮复现 | 配置项多,门槛偏高 |
| 5 | GTmetrix | 在线 | 竞品横评、生成报告 | 免费额度有限 |
| 6 | Pingdom | 监控 | 可用性加性能双监控 | 告警需分级,否则噪音大 |
| 7 | SpeedCurve | 监控 | 趋势分析、发布关联 | 偏团队协作,单人成本高 |
| 8 | Calibre | 监控 | 预算设定、回归拦截 | 需要先定预算基线 |
| 9 | sitespeed.io | 合成 | 批量跑点、自定义脚本 | 依赖容器环境 |
| 10 | k6 浏览器模块 | 压测 | 并发下的性能表现 | 侧重负载,不擅长单页诊断 |
| 11 | 自研 Puppeteer 脚本 | 合成 | 定制化采集逻辑 | 维护成本高 |
| 12 | web-vitals 类库 | RUM | 真实用户指标采集 | 需要自建聚合端 |
| 13 | curl / httpstat | 协议层 | 定位网络与服务端分界 | 不反映渲染与交互 |
这张表的用法不是“选一个”,而是“选一组”。我的常规组合是:Lighthouse 做发布前的硬门槛,WebPageTest 做疑难问题的深度复现,RUM 埋点做长期趋势,curl 做线上突发问题的第一现场判断。四类工具覆盖了从秒级到毫秒级、从合成到真实的全区间。
3. 指标拆解:TTFB、LCP、INP、CLS 到底怎么读
3.1 首字节时间的六段分解与达标线
首字节时间这个指标经常被当成一个整体来看,实际上它至少能拆成六段:DNS 解析、TCP 握手、TLS 握手、请求发送、服务端处理、首字节回传。每一段的优化手段完全不同,不拆开就不知道该找谁。
拿一个具体的例子算一遍。假设用户到服务端的网络往返延迟是 30 毫秒,DNS 解析走了 12 毫秒(命中本地缓存),TCP 握手一个往返 30 毫秒,TLS 1.3 握手一个往返 30 毫秒,请求发送 1 毫秒,服务端处理 80 毫秒,首字节回传 15 毫秒。加起来是 168 毫秒。这个数字是健康的。
现在把用户换到跨区域访问,往返延迟变成 150 毫秒。DNS、TCP、TLS 三段就变成 12 加 150 加 150,光建立连接就 312 毫秒,加上服务端处理和回传,首字节时间直接冲到 400 毫秒以上。这时候你去优化服务端代码,把 80 毫秒的处理降到 40 毫秒,总时间只降到 360 毫秒左右,收益微乎其微。真正该做的是把静态资源和动态接口就近部署、缩短往返次数、把 TLS 握手合并到 TCP 握手里。
这就是为什么我一直强调“先拆再优化”。行业里比较通用的分档是:首字节时间在 200 毫秒以内算优秀,200 到 500 毫秒算合格,500 到 1000 毫秒需要关注,超过 1000 毫秒就是明显问题。但这个分档要结合业务场景调整,后台管理系统和面向公众的内容站,容忍度完全不一样。
连接复用对首字节时间的影响也值得单独说。HTTP/1.1 时代每个域名并发连接有限,页面里几十个资源会排队等连接,排队时间也算进了等待阶段。HTTP/2 的多路复用把这个排队问题解决了大半,但首字节时间里的握手成本依然存在,只是从“每个请求一次”变成了“每个连接一次”。所以看首字节时间一定要区分首次访问和二次访问,混在一起看会得出错误结论。
3.2 核心网页指标的阈值与采样口径
最大内容绘制衡量的是“主要内容什么时候渲染完”,阈值是 2.5 秒以内算好、4 秒以上算差。这个指标容易踩的坑是“元素会变”。页面加载过程中,如果最大的一块内容在中途被替换掉(比如骨架屏换成真实数据),浏览器会重新计算,最终记录的是最晚那次。这就意味着骨架屏高度和真实内容高度不一致时,指标会被拉高。
交互延迟衡量的是用户点击到界面响应的时间,阈值是 200 毫秒以内。它的测量方式比较特殊,是取一次访问中所有交互里最差的那个值,而不是平均值。所以优化的时候要盯着最差的那次交互,通常是首次加载时主线程被长任务占住的那一段。把长任务拆成小块、把非关键脚本延后执行,是最直接的手段。
累积布局偏移衡量的是页面元素的意外位移,阈值是 0.1 以内。它的计算方式有一个“会话窗口”的概念:间隔小于一秒、总时长不超过五秒的一系列位移算作一次,取所有窗口里最差的那次。这个设计是为了避免长时间缓慢的微小位移累加成高分。实际排查时,八成以上的偏移来自图片没写尺寸、字体加载导致文字重排、动态插入的横幅把内容顶下去这三类。
采样口径上有几个原则。第一,看分位数不看平均值,业界的共识是用第七十五分位作为达标线,意思是“至少百分之七十五的访问达到这个水平”。第二,要排除后退缓存恢复的页面,这类页面从缓存里瞬间恢复,指标会被严重美化,不代表真实加载。第三,要区分首次访问和回访,首次访问没有缓存,指标天然更差,把所有访问混在一起算会掩盖首次访问的问题。
注意:如果页面在加载过程中用户切走了标签页,最大内容绘制的记录会停在那里,不会继续累计。这类样本要不要剔除,取决于你想衡量的是“完整加载体验”还是“用户实际看到的部分”,两种口径都合理,但必须统一。
3.3 从瀑布图读出瓶颈归属
瀑布图是所有测速工具里信息密度最高的一张图。横轴是时间,纵轴是请求,每个请求画成一条横条,不同颜色代表不同阶段。看懂它的关键在于找“长条”和“空档”。
长条代表单个请求耗时长。如果一条长条的前半段是等待色(通常是服务端处理),说明后端慢;如果后半段是下载色且很长,说明资源太大或者带宽不足。空档代表浏览器在等什么——要么在等主线程空闲,要么在等上一个请求的依赖关系,要么在等 DNS 解析。大量空档出现在首屏关键请求之间,通常意味着请求链太深,得做资源预加载或者接口合并。
还有一个容易被忽略的细节是请求的起始时间。如果一批请求都在同一个时间点开始,说明它们是并发发起的;如果它们像阶梯一样依次开始,说明存在串行依赖,这时候要检查是不是接口之间有数据依赖,能不能改成并行。
我排查线上问题时有个固定套路:先把瀑布图按“开始时间”排序,看第一个请求到最后一个关键请求之间的跨度;再按“耗时”排序,找出耗时最长的三个请求;最后看这三个请求分别属于哪个域名、哪个服务。这个套路能覆盖大部分“页面为什么慢”的问题,而且不需要任何额外的埋点。
4. 实操:搭一条可复现的测速流水线
4.1 环境准备与最小可用验证
从零开始搭,第一步是准备一台能跑无头浏览器的机器。要求不高,两核四G足够跑单线程任务,但要注意两个前提:系统里要有中文字体,否则截图和文字渲染会出问题;时区要设成目标用户所在时区,避免因为时间格式差异导致页面渲染异常。
先装 Node 环境,然后装 Lighthouse 和 sitespeed.io。后者依赖 Docker,用容器方式跑最省事:
# 验证 node 环境 node -v # 安装 lighthouse npm install -g lighthouse # 拉取 sitespeed.io 镜像 docker pull sitespeedio/sitespeed.io装完之后先做一个最小验证,跑一轮 Lighthouse 看能不能出报告。如果这一步失败,八成是 Chrome 启动参数的问题,加上--no-sandbox和--disable-dev-shm-usage通常能解决。这两个参数的含义分别是关闭沙箱和改用内存文件系统,前者是因为容器里没有权限隔离,后者是因为容器默认的共享内存太小,大页面会直接崩掉。
验证通过之后,把测试目标固定下来。我的做法是维护一份页面清单,包含首页、列表页、详情页、登录后的核心页四类,每类挑一到两个代表。清单要写进版本库,和代码一起管理,这样每次加页面都有记录,不会出现“测的页面早就下线了”这种尴尬。
4.2 批量跑点与结果落库
单个页面跑一轮不够,同一页面要跑多次取中位数,多个页面要并行跑。sitespeed.io 在这块比较顺手,它支持配置文件和预算检查:
docker run --rm -v "$(pwd):/sitespeed.io" \ sitespeedio/sitespeed.io:latest \ https://example.com \ https://example.com/list \ -n 3 \ --budget.configPath /sitespeed.io/budget.json \ --outputFolder /sitespeed.io/result-n 3表示每个页面跑三轮,取中位数作为最终结果。三轮是个经验值,少于三轮波动太大,多于三轮边际收益递减还费时间。预算文件用来定义指标门槛,比如首字节时间不超过 500 毫秒、最大内容绘制不超过 2.5 秒,超了就让命令返回非零退出码,接在流水线里就能自动卡住发布。
结果落库这块,我的建议是别急着上时序数据库。初期用 JSON 文件加日期目录就够了,一个页面一次测试存一个文件,文件名带上时间戳和页面标识。等数据量大到文件检索不方便了,再往数据库里搬。这个顺序反了的话,你会花大量时间在数据管道上,而真正需要看的数据其实没多少。
结果文件里至少要保留这些字段:页面标识、测试时间、设备类型、网络条件、首字节时间、最大内容绘制、交互延迟、布局偏移、总请求数、总传输大小。字段定好之后加一个简单的汇总脚本,把同一天的多次结果取中位数,输出成一张表。这张表就是周会上的唯一数据来源。
4.3 埋点接入与数据质量校验
RUM 埋点接入的关键不是代码怎么写,而是怎么保证数据可信。我见过太多埋点上线之后数据全是垃圾的案例,原因基本集中在三类。
第一类是上报地址写错或者被拦截。埋点请求走的是业务域名,一旦被安全策略或者浏览器拦截,数据就静默丢失,前端还不会报错。验证方法是上线后立刻在浏览器网络面板里看有没有请求发出去、返回码是不是两百。
第二类是重复上报。单页应用里路由切换会触发多次初始化,同一份数据被报了两遍。解决办法是在全局加一个标记,只在首次加载时初始化采集,路由切换时只重置页面标识,不重新注册监听。
第三类是页面卸载时数据丢失。用户很快关掉页面,指标还没上报完。前面提到的sendBeacon就是解决这个的,但要注意它也有体积限制,单次请求别塞太多字段。
数据校验的做法是抽样比对。挑几个真实的访问,在浏览器里手动跑一遍测量,把结果和埋点上报的数据对一对。如果差得离谱,先查是不是采集时机不对,比如在页面还在加载时就取了指标值。
4.4 告警阈值怎么定才不吵人
阈值定得太松,出问题没人知道;定得太紧,天天响没人看。我的办法是从历史数据反推:先让监控跑两周只记录不告警,把这两周的数据按页面算第七十五分位和第九十五分位,然后拿第七十五分位乘一点二作为日常告警线,拿第九十五分位作为严重告警线。
举个具体的:某列表页首字节时间的第七十五分位是 380 毫秒,第九十五分位是 720 毫秒。那日常告警线设 450 毫秒,严重告警线设 750 毫秒。超过日常线发到团队频道,超过严重线直接电话。这套阈值不是拍脑袋来的,是从自己的数据里长出来的,误报率会低很多。
还要设一条“突变”规则:连续三个采样点都比前一周同期高出百分之五十以上,不管有没有超阈值都报一次。这条规则抓的是那种绝对值还不高但趋势异常的情况,往往能提前发现资源泄漏或者缓存失效这类问题。
5. 常见问题排查实录
5.1 实验室数据和真实数据打架怎么办
这是最常被问到的问题:Lighthouse 给 92 分,RUM 显示最大内容绘制第七十五分位三秒多。两边都没错,只是测的不是同一件事。
实验室数据跑在干净的环境里,缓存是冷的、网络是固定的、没有第三方脚本干扰、设备性能是模拟的。真实用户那边,缓存可能被禁用了、网络在切换、页面上挂着广告和统计脚本、手机可能正在后台跑别的应用。差距大恰恰说明你的页面在弱环境下退化严重。
处理思路是先按设备类型把 RUM 数据切开,看是不是集中在低端机型上。如果是,就去做真机测试,把 CPU 降速调到六倍甚至更低,看页面还能不能撑住。如果集中在某个网络类型上,就去检查资源体积和请求数,砍掉不必要的第三方脚本。如果集中在某个地域,就去看 CDN 的节点覆盖和缓存命中率。
还有一种情况是实验室数据比真实数据还差。这通常是因为测试节点选得太偏,或者测试时恰好碰上了服务抖动。这种情况先别急着优化,把测试重跑几轮看是否复现,不复现就是噪音。
5.2 指标突然变好,先别高兴
有一次改完一轮优化,某个页面的最大内容绘制从 3.2 秒掉到 1.8 秒,团队正准备庆祝,我发现采样量从每天两千掉到了八十。原因是新版本上线时埋点脚本的位置被调到了某个条件分支里,只有特定用户才会触发。数据变好不是因为变快,是因为慢的那批用户压根没被统计到。
这类问题有个统一的识别方法:看采样量。任何指标趋势图旁边都要并排放一张采样量图,采样量突变的时候,指标数据一律不可信。另外一个信号是分布形状,正常情况下分布是一条长尾曲线,如果突然变成了两个尖峰或者断崖,说明用户构成变了。
还有一种隐蔽的情况是缓存策略改动导致的。比如给某个静态资源配置了更长的缓存时间,回访用户的加载速度快了很多,整体指标变好,但首次访问的用户体验一点没变。这时候要把首次访问和回访分开看,否则会误判优化效果。
5.3 排查速查表
| 现象 | 优先怀疑 | 验证方法 | 处理方向 |
|---|---|---|---|
| 首字节时间高但下载快 | 服务端处理慢或连接建立慢 | 用协议层工具拆各阶段耗时 | 缩短往返、就近部署、优化后端查询 |
| 首屏出现晚但首字节正常 | 渲染被阻塞 | 看瀑布图首屏前的请求链 | 内联关键样式、延后非关键脚本 |
| 交互延迟高 | 主线程长任务 | 性能面板看长任务分布 | 拆分任务、用空闲回调、减少同步计算 |
| 布局偏移大 | 图片无尺寸、字体替换 | 逐帧回放看位移来源 | 预留占位尺寸、字体预加载 |
| 实验室好真实差 | 弱网弱机退化 | 按设备与网络切分真实数据 | 降资源体积、减第三方脚本 |
| 指标忽高忽低 | 采样量不足或节点波动 | 看采样量曲线与标准差 | 增加测试轮次、固定测试节点 |
| 优化后数据变好但用户仍抱怨 | 统计口径变化 | 对比优化前后的采样构成 | 统一定义、分开统计首次与回访 |
5.4 几个不太上台面但很管用的经验
测速这件事有很多细节文档里不会写。比如测移动端性能时,真机永远比模拟器准,但真机测试成本高,我的折中办法是先用模拟器筛出问题页面,再用两三台不同档位的真机做最终确认。模拟器能把问题放大,真机能告诉你放大的倍数对不对。
再比如测速的时间窗口。工作日白天和深夜的数据差异可能很大,因为深夜的请求量小、服务端压力低。如果你的监控只在固定时间跑,很容易错过高峰期的问题。我的做法是至少覆盖早中晚三个时段,重要页面再加一个凌晨的基线对照。
还有就是别迷信工具给的“优化建议”。工具能看出图片没压缩、文本没开压缩,但它不知道这张图片是业务方要求的高清大图,也不知道这段文本压缩后会触发某个中间层的兼容问题。建议清单是排查的起点,不是执行的终点,每一条都要过一遍业务判断。
最后分享一个我一直在用的习惯:每次做完一轮优化,把优化前的截图、优化后的截图、对应的指标数据和时间点记在一个文档里,按时间倒序排列。这个文档攒到二三十条之后,就成了团队里最有价值的东西——下次遇到类似问题,翻一翻就知道当初是怎么解决的、踩过哪些坑、哪些方案最后被回滚了。工具会换、指标定义会调整,但这份记录一直在。