前端监控系列3 | 如何衡量一个站点的性能好坏

标签: 前端 监控 系列 | 发表时间:2022-09-14 19:50 | 作者:字节跳动技术团队
出处:https://juejin.cn/frontend

1. 背景

你知道有多少用户没等到页面首屏出现就离开了吗?性能不佳会对业务目标产生负面影响。比如, BBC 发现他们的网站加载时间每增加一秒,他们就会失去 10% 的用户。 高性能站点比低性能站点更能吸引和留住用户,而留住用户对于提高用户转化率至关重要。

这篇文章就是以此为背景,介绍字节内部是如何衡量站点性能的,如何依靠性能监控定位线上站点性能问题的。

2. 如何衡量站点性能

站点性能好坏的表现形式是多样的,不是单纯通过页面加载速度、页面渲染速度就能衡量, 而是要关注从页面开始加载到被关闭的整个过程中,用户对性能的感知。一个页面,即使很快渲染,如果对用户的交互迟迟没有响应,那么在用户心中这个站点的性能依然很差。

站点性能一般可以分为两类,一类是首屏性能,另一类是运行时性能。前者衡量的是页面从加载开始到可以稳定交互的性能情况,后者衡量的是页面稳定后到页面关闭的性能情况。

3. 首屏性能

早在 2012 年, Web 性能工作组[1] 就针对页面加载场景制定了加载过程模型,用来衡量页面加载各个阶段的耗时情况,从而衡量页面加载的性能。具体的加载过程模型如图所示:

图片

这个模型定义了页面加载开始到页面加载完成整个过程的各个时间点,除了正常的初始化并且拉取到主文档的时间点以外,还包括了解析渲染的详细时间点。比如:

  • domLoading 代表 开始解析的时间点
  • domInteractive 代表 DOM 解析完成、开始加载内嵌资源的时间点
  • domComplete 代表 文档解析完成的时间点
  • loadEventStart 代表  load 事件被触发的时间点

虽然开发者可以 根据 这些时间点 来衡量页面加载时的性能情况,但是线上用户其实感知不到这些时间点的区别。 对于用户而言,只能感知到页面何时开始渲染、何时渲染出主要内容、何时可以交互、以及交互时是否有延迟。

那么针对用户感知到的这四个阶段,有没有可用于衡量的指标呢?

3.1 何时开始渲染:FP && FCP

  • FP:First Paint,首次渲染的时间点。FP 时间点之前,用户看到的都是没有任何内容的白色屏幕。
  • FCP:First Contentful Paint,首次有实际内容渲染的时间点。

这两个指标都来源于 Paint Timing [2] 标准, 这个标准主要是记录在页面加载期间的一些关键时间点。通过这两个指标,就可以衡量页面何时开始渲染内容了。

3.2 何时渲染出主要内容:FMP && LCP && SI

  • FMP:First Meaningful Paint,完成首次有意义内容绘制的时间点。
  • LCP:Largest Contentful Paint,最大的内容在可视区域内变得可见的时间点。
  • SI:Speed Index,衡量页面可视区域的加载速度,反映页面的加载体验差异。

有了这三个指标,就可以衡量页面何时渲染出主要内容了。不过业界有测试得出,LCP 非常近似于 FMP 的时间点,同时 FMP 性能消耗较大,且会因为一些细小的变化导致数值巨大波动,所以 推荐使用 LCP 。  而 SI 因为计算复杂,指标难以解释,所以一般只在实验室环境下使用。

3.3 何时可以交互:TTI && TBT

  • TTI: Time to Interactive,页面从开始加载到主要子资源完成渲染,并能够快速、可靠地响应用户输入的时间点。
  • TBT: Total Blocking Time,页面从 FCP 到 TTI 之间的阻塞时间,一般用来量化主线程在空闲之前的繁忙程度。

TTI 虽然可以衡量页面可以交互的时间点,但是却无法感知这个期间浏览器的繁忙状态。而结合 TBT ,就能帮助理解在加载期间,页面无法响应用户输入的时间有多久。

3.4 交互时是否有延迟:FID && MPFID

  • FID:First Input Delay,用户第一次与页面交互(例如当他们单击链接、点按按钮等操作)直到浏览器对交互作出响应,并且实际能够开始处理事件程序所经过的时间。
  • MPFID: Max Potential First Input Delay,记录在页面加载过程中用户和页面进行首次交互操作可能花费的最长时间。

MPFID  是一个虚拟的可能的延迟时间,而  FID  是用户真实的首次交互的延迟时间。所以一般推荐使用FID,它是用户对页面交互性和响应性的第一印象。良好的第一印象有助于用户建立对整个应用的良好印象。同时在页面加载阶段,资源的处理任务最重,最容易产生输入延迟。

至此,通过上面每个阶段的指标,基本可以实现全面衡量首屏性能。那么运行时的性能又可以怎样衡量呢?

4. 运行时性能

运行时性能一般可以通过Long tasks 来感知。Long tasks主要是衡量主线程的繁忙情况。

4.1 Long tasks

如果一个任务在主线程上运行超过 50 毫秒,那么它就是 Long task。如果可以收集到运行时的所有Long tasks,就能知道运行时的性能情况。 在具体实践中,可以关注耗时较长的Long task,将它和用户行为关联在一起,可以有效帮助定位线上卡顿的原因。

5. 性能指标的计算原理

页面性能相关的指标都有了,那么如何采集这些数据呢?

5.1 采集页面加载过程的各阶段耗时

页面加载过程中的时间点主要依赖 Navigation Timing [4] 标准,这个标准后来升级到了2.0版本, 对应 Navigation Timing 2 [5] 标准,两者虽然不尽相同,但是可计算出的指标基本一致。在浏览器中可以通过下面的方式获取:

  // navigation timing
const timing = window.performance.timing

// navigation timing 2
performance.getEntriesByType('navigation')

图片

基于这些数据,不仅可以计算出 DNS / TCP / Request 等耗时,还可以计算出 DOMReady / DOMParse / Load 等耗时。

图片

5.2 采集 FP && FCP

FP 和 FCP 可以通过浏览器提供的 API 直接获取,所以采集原理并不复杂。如果页面已经完成了首次绘制和首次内容绘制,可以使用下面的方式直接获取。

  window.performance.getEntriesByType('paint')
// or
window.performance.getEntriesByName('first-paint')
window.performance.getEntriesByName('first-contentful-paint')

但是如果页面还没有开始首次绘制,就需要通过监听获取。

  const observer = new PerformanceObserver(function(list) {
  const perfEntries = list.getEntries();
  for (const perfEntry of perfEntries) {
      // Process entries
      // report back for analytics and monitoring
      // ...
  }
});

// register observer for paint timing notifications
observer.observe({entryTypes: ["paint"]});

5.3 采集 LCP

LCP 主要依赖 PerformanceObserver,具体监听方式如下:

  new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('LCP candidate:', entry.startTime, entry);
  }
}).observe({type: 'largest-contentful-paint', buffered: true});

浏览器会多次报告 LCP ,而一般真正的 LCP 是用户交互前最近一次报告的 LCP ,因为交互往往会改变用户可见的内容,所以用户交互后新报告的 LCP 不再符合 LCP 的指标定义。

5.4 采集 FMP

与 FP / FCP / LCP 相比,  FMP 的采集相对比较复杂,它需要通过算法计算得出,而业界并没有统一的算法。不过比较认可的一个计算 FMP 的方式是「认定页面在加载和渲染过程中最大布局变动之后的那个绘制时间即为当前页面的 FMP 」。

由于在页面渲染过程中,「 DOM 结构变化的时间点」和与之对应的「渲染的时间点」近似相同,所以字节内部计算 FMP 的方式是:计算出 DOM 结构变化最剧烈的时间点,即为 FMP。

具体步骤为:

  1. 通过 MutationObserver 监听每一次页面整体的 DOM 变化,触发 MutationObserver 的回调
  1. 在回调计算出当前 DOM 树的分数
  1. 在结算时,通过对比得出分数变化最剧烈的时刻,即为 FMP

5.5 采集 TTI && TBT

与 FMP 相似,浏览器也没有提供直接获取 TTI 的 API ,不过针对如何计算 TTI 有详细的描述,实现对应描述就可以得出 TTI 的时间点。

具体的描述是:先找到 FCP 的时间点,再往前找到一个安静窗口。安静窗口需要满足三个条件: 1) 没有 Long task。2)窗口中正在处理的 GET 请求不超过两个。3) 窗口时间窗读应该至少 5s。

窗口前的最后一个长任务的结束时间就是 TTI 。

图片

而通过计算 FCP 和 TTI 之间的长任务的阻塞时间的总和,就能得出 TBT 。

阻塞时间是 Long task 中超过 50ms 后的任务耗时。

图片

5.6 采集 FID && MPFID

FID 同样依赖 PerformanceObserver,具体监听方式如下:

  new PerformanceObserver(function(list, obs) {
  const firstInput = list.getEntries()[0];

  // Measure the delay to begin processing the first input event.
  const firstInputDelay = firstInput.processingStart - firstInput.startTime;
  // Measure the duration of processing the first input event.
  // Only use when the important event handling work is done synchronously in the handlers.
  const firstInputDuration = firstInput.duration;
  // Obtain some information about the target of this event, such as the id.
  const targetId = firstInput.target ? firstInput.target.id : 'unknown-target';
  // Process the first input delay and perhaps its duration...

  // Disconnect this observer since callback is only triggered once.
  obs.disconnect();
}).observe({type: 'first-input', buffered: true});

MPFID 是 FCP 之后最长的长任务耗时,可以通过监听 FCP 之后的 Long tasks,对比拿到最长的长任务就是 MPFID 。

虽然浏览器提供了 足够的 API 来帮助采集各个性能指标,但是在 JS 中计算具体指标要更为复杂。原因有两点:一是 API 报告的内容和指标本身的定义有部分差异,所以计算时要处理这些差异;二是 部分场景下浏览器不会报告对应内容,这些场景下需要模拟测量。

6. 怎样评估站点整体的性能好坏

虽然有众多性能指标,但是每个性能指标评估的都是单一方面,如何整体来看站点的性能是好是坏呢?  对于每个单一指标,是否有标准去定义指标的值在具体哪个范围内能算优秀?线上的站点性能应该重点考量哪些性能指标?各个性能指标的权重占多少合适呢?

6.1 性能指标基准线

Google 提供了各个性能指标的基准线,有一定的参考意义。

为什么只说是有一定参考意义?首先基准线本身是在变化的,随着指标计算的逐渐更新以及软件硬件的更新,基准线也会有一定的调整。其次用户的使用场景对性能指标也会有很大的影响,比如 iOS 用户上报的性能指标一般都优于 Android 用户上报的性能指标。

下方是目前字节内部使用的部分性能指标基准线,基本对齐 Google 建议的基准线,通过这些数据可以分析站点的性能达标率情况。

Metric Name Good(ms) Needs Improvement(ms) Poor(ms)
FP 0-1000 1000-2500 Over 2500
FCP 0-1800 1800-3000 Over 3000
LCP 0-2500 2500-4000 Over 4000
TTI 0-3800 3800-7300 Over 7300
FID 0-100 100-300 Over 300

6.2 衡量站点满意度

站点满意度的衡量除了要考虑常规的性能指标外,还要考虑体验类的指标,比如专门衡量视觉稳定性的指标 CLS。

线上的站点满意度衡量,一般会在参考lighthouse的满意度计算规则的基础上,去除一些推荐在实验室环境测量的指标的权重。

下方是目前字节使用的线上站点性能满意度的权重计算公式,去除了SI 和 TBT这两个不推荐在线上环境测量的指标。

图片

那么有了一个站点满意度以后,我们终于能知道一个站点的性能好坏了。那么假设性能不好,我们应该怎样优化?

7. 如何优化站点性能

通常,我们可以从性能指标下手去做针对性的优化。 虽然指标各不相同,但是优化的思路是相通的。在了解清楚指标的依赖项以后,通过优化指标的相关依赖项,就能快速优化性能指标,从而提升站点性能。

说起来比较抽象,举个例子:比如当我们想要优化 TTI ,根据刚刚提到的 TTI 的计算方式,可以得出 TTI 的依赖项主要包含 FCP 、请求和 Long tasks,那么尽快的渲染、尽早的请求、请求尽快结束、避免长任务就是优化的关键。关于指标的具体优化措施的内容比较多,将在后续的文章中展开介绍。

了解全面的优化措施固然重要,但把每个措施都做一遍并不一定能够高效地解决站点面临的关键性能问题。 如何立竿见影、具有针对性的去优化?通过还原用户加载时的情况来帮助定位性能问题是一个思路。

8. 利用线上监控定位性能问题

一般情况下,前端监控除了监控性能指标以外,还会监控请求和资源的加载以及 Long tasks 等数据,这些数据可以帮助还原用户的加载现场,帮助找到蛛丝马迹。

比如下面这个例子, 多项性能指标都很差。 通过监控平台还原出的资源加载瀑布图,  可以看出绝大部分时间都耗在了拉取资源上。那么就可以初步得出性能优化方案,将优化措施侧重在资源优化上,比如缩小 JS 文件体积、延迟加载未使用的 JS 代码等等。

图片

线上监控帮助定位性能问题的例子还有很多,这里不一一介绍了。截图中使用的是字节内部的前端监控平台,目前这套解决方案已同步在火山引擎上,接入即可对 Web 端真实数据进行实时监控、报警归因、聚类分析和细节定位,解决白屏、性能瓶颈、慢查询等关键问题,欢迎体验。 点击立即申请免费使用

9. 相关资料

[1] Web性能工作组:https://www.w3.org/webperf/

[2] Paint Timing:https://w3c.github.io/paint-timing/

[3] Event Timing: https://w3c.github.io/event-timing/

[4] Navigation Timing: https://www.w3.org/TR/navigation-timing/

[5] Navigation Timing 2: https://www.w3.org/TR/navigation-timing-2/

相关 [前端 监控 系列] 推荐:

前端监控系列3 | 如何衡量一个站点的性能好坏

- - 掘金 前端
你知道有多少用户没等到页面首屏出现就离开了吗. 性能不佳会对业务目标产生负面影响. 比如, BBC 发现他们的网站加载时间每增加一秒,他们就会失去 10% 的用户. 高性能站点比低性能站点更能吸引和留住用户,而留住用户对于提高用户转化率至关重要. 这篇文章就是以此为背景,介绍字节内部是如何衡量站点性能的,如何依靠性能监控定位线上站点性能问题的.

前端性能监控系统ShowSlow

- - CSDN博客Web前端推荐文章
作者:zhanhailiang 日期:2014-11-14. ShowSlow是开源的前端性能监控系统,提供了以下功能:. 前端性能指标数据收集功能:ShowSlow原生提供了数据收集工具. DOM Monster!,但也支持通过YSlow,PageSpeed等第三方工具将性能数据上报给服务端完成收集(其服务器端提供了针对多达8种不同工具上报的数据收集器dommonster,dynatrace,events,har,metric,pagespeed,webpagetest,yslow);.

搭建前端异常监控系统

- - 掘金 架构
收集前端错误(原生、React、Vue). 利用Egg.js编写一个错误日志采集服务. 编写webpack插件自动上传sourcemap. 利用sourcemap还原压缩代码源码位置. 代码上线打包将sourcemap文件上传至错误监控服务器. 发生错误时监控服务器接收错误并记录到日志中. 根据sourcemap和错误日志内容进行错误分析.

前端监控系统设计

- - 掘金 前端
前言: 创建一个可随意插拔的插件式前端监控系统. 使用window.addEventListener('error',cb). 由于这个方法会捕获到很多error,所以我们要从中筛选出静态资源文件加载错误情况,这里只监控了js、css、img. // 捕获静态资源加载失败错误 js css img window.addEventListener('error', e => {.

监控Tomcat方案调研(监控应用服务器系列文章一)

- - 博客园_首页
前言: 最近在做一个监控应用服务器(Tocmat、WebSphere、WebLogic)的项目,目前已小有规模,回头看看,一路走来,也算是磕磕绊绊,遇到过种种问题,走过不少弯路,不过程序员最不怕的就是遇到问题——有什么问题就解决什么问题. 为了给后来人留下点经验之谈,助之少走弯路,特意把这些经验整理出来,与大家分享.

监控平台前端SDK开发实践

- - 美团点评技术团队
监控是提高故障处理能力和保障服务质量必需的一环,它需要负责的内容包括:及时上报错误、收集有效信息、提供故障排查依据. 及时上报错误:发生线上问题后,经由运营或者产品反馈到开发人员,其中流转过程可能是几分钟甚至几十分钟,这段时间可能直接导致公司的经济损失. 如果有一个监控系统,在线上出现问题时,监控系统能够第一时间报警,并且通知到开发人员,那开发人员就可以第一时间修复上线,使公司损失最小化.

前端异常监控解决方案研究

- - 腾讯CDC
前端监控包括行为监控、 异常监控、性能监控等,本文主要讨论异常监控. 对于前端而言,和后端处于同一个监控系统中,前端有自己的监控方案,后端也有自己等监控方案,但两者并不分离,因为一个用户在操作应用过程中如果出现异常,有可能是前端引起,也有可能是后端引起,需要有一个机制,将前后端串联起来,使监控本身统一于监控系统.

前端监控实践——FMP的智能获取算法

- - SegmentFault 最新的文章
今天来给大家介绍下前端监控中一个特定指标的获取算法,有人会问,为啥就单单讲一个指标. 这是因为,目前大部分的指标,比如白屏时间,dom加载时间等等,都能通过现代浏览器提供的各种api去进行较为精确的获取,而今天讲的这个指标,以往获取他的方式只能是通过逻辑埋点去获取它的值,因此在做一些前端监控时,需要根据业务需要去改变页面对这个值的埋点方式,会比较繁琐,恰巧最近刚刚好在做一些前端监控相关的项目,遇到这个问题时就在想,能不能通过一种无须埋点的方式,将这个值给获取到.

为什么前端监控要用 GIF 打点

- - IT瘾-tuicool
为什么前端监控要用GIF打点?. 我们知道,目前主流的前端监控(百度统计/友盟/谷歌统计)都在用GIF进行打点. 但是,为什么这些系统都会使用GIF,难道是因为没有其他的解决方案吗. 所谓的前端监控,其实是在满足一定条件后,由Web页面将用户信息(UA/鼠标点击位置/页面报错/停留时长/etc)上报给服务器的过程.

前端异常监控解决方案研究

- - IT瘾-dev
摘要:异常监控不复杂也不简单啊…. 作者:frustigor. 前端监控包括行为监控、 异常监控、性能监控等,本文主要讨论异常监控. 对于前端而言,和后端处于同一个监控系统中,前端有自己的监控方案,后端也有自己等监控方案,但两者并不分离,因为一个用户在操作应用过程中如果出现异常,有可能是前端引起,也有可能是后端引起,需要有一个机制,将前后端串联起来,使监控本身统一于监控系统.