发布网友 发布时间:2024-09-05 02:01
共1个回答
热心网友 时间:2024-09-05 02:32
作者:彭莉,火山引擎APM研发工程师。2020年加入字节,负责前端监控SDK的开发维护、平台数据消费的探索和落地。
背景你知道有多少用户没等到页面首屏出现就离开了吗?性能不佳会对业务目标产生负面影响。比如,BBC发现他们的网站加载时间每增加一秒,他们就会失去10%的用户。高性能站点比低性能站点更能吸引和留住用户,而留住用户对于提高用户转化率至关重要。
这篇文章就是以此为背景,介绍字节内部是如何衡量站点性能的,如何依靠性能监控定位线上站点性能问题的。
如何衡量站点性能站点性能好坏的表现形式是多样的,不是单纯通过页面加载速度、页面渲染速度就能衡量,而是要关注从页面开始加载到被关闭的整个过程中,用户对性能的感知。一个页面,即使很快渲染,如果对用户的交互迟迟没有响应,那么在用户心中这个站点的性能依然很差。
站点性能一般可以分为两类,一类是首屏性能,另一类是运行时性能。前者衡量的是页面从加载开始到可以稳定交互的性能情况,后者衡量的是页面稳定后到页面关闭的性能情况。
首屏性能早在2012年,Web性能工作组[1]就针对页面加载场景制定了加载过程模型,用来衡量页面加载各个阶段的耗时情况,从而衡量页面加载的性能。具体的加载过程模型如图所示:
这个模型定义了页面加载开始到页面加载完成整个过程的各个时间点,除了正常的初始化并且拉取到主文档的时间点以外,还包括了解析渲染的详细时间点。比如:
domLoading代表开始解析的时间点
domInteractive代表DOM解析完成、开始加载内嵌资源的时间点
domComplete代表文档解析完成的时间点
loadEventStart代表load事件被触发的时间点
虽然开发者可以根据这些时间点来衡量页面加载时的性能情况,但是线上用户其实感知不到这些时间点的区别。对于用户而言,只能感知到页面何时开始渲染、何时渲染出主要内容、何时可以交互、以及交互时是否有延迟。
那么针对用户感知到的这四个阶段,有没有可用于衡量的指标呢?
何时开始渲染:FP&&FCPFP:FirstPaint,首次渲染的时间点。FP时间点之前,用户看到的都是没有任何内容的白色屏幕。
FCP:FirstContentfulPaint,首次有实际内容渲染的时间点。
这两个指标都来源于PaintTiming[2]标准,这个标准主要是记录在页面加载期间的一些关键时间点。通过这两个指标,就可以衡量页面何时开始渲染内容了。
何时渲染出主要内容:FMP&&LCP&&SIFMP:FirstMeaningfulPaint,完成首次有意义内容绘制的时间点。
LCP:LargestContentfulPaint,最大的内容在可视区域内变得可见的时间点。
SI:SpeedIndex,衡量页面可视区域的加载速度,反映页面的加载体验差异。
有了这三个指标,就可以衡量页面何时渲染出主要内容了。不过业界有测试得出,LCP非常近似于FMP的时间点,同时FMP性能消耗较大,且会因为一些细小的变化导致数值巨大波动,所以推荐使用LCP。而SI因为计算复杂,指标难以解释,所以一般只在实验室环境下使用。
何时可以交互:TTI&&TBTTTI:TimetoInteractive,页面从开始加载到主要子资源完成渲染,并能够快速、可靠地响应用户输入的时间点。
TBT:TotalBlockingTime,页面从FCP到TTI之间的阻塞时间,一般用来量化主线程在空闲之前的繁忙程度。
TTI虽然可以衡量页面可以交互的时间点,但是却无法感知这个期间浏览器的繁忙状态。而结合TBT,就能帮助理解在加载期间,页面无法响应用户输入的时间有多久。
交互时是否有延迟:FID&&MPFIDFID:FirstInputDelay,用户第一次与页面交互(例如当他们单击链接、点按按钮等操作)直到浏览器对交互作出响应,并且实际能够开始处理事件程序所经过的时间。
MPFID:MaxPotentialFirstInputDelay,记录在页面加载过程中用户和页面进行首次交互操作可能花费的最长时间。
MPFID是一个虚拟的可能的延迟时间,而**FID**是用户真实的首次交互的延迟时间。所以一般推荐使用FID,它是用户对页面交互性和响应性的第一印象。良好的第一印象有助于用户建立对整个应用的良好印象。同时在页面加载阶段,资源的处理任务最重,最容易产生输入延迟。
至此,通过上面每个阶段的指标,基本可以实现全面衡量首屏性能。那么运行时的性能又可以怎样衡量呢?
运行时性能运行时性能一般可以通过Longtasks和InputDelay来感知。Longtasks主要是衡量主线程的繁忙情况,而InputDelay主要是衡量用户交互的延迟情况。
Longtasks如果一个任务在主线程上运行超过50毫秒,那么它就是Longtask。如果可以收集到运行时的所有Longtasks,就能知道运行时的性能情况。在具体实践中,可以关注耗时较长的Longtask,将它和用户行为关联在一起,可以有效帮助定位线上卡顿的原因。
InputDelay它源于EventTiming[3]标准,这个标准主要是帮助深入了解由用户交互触发的某些事件的延迟,通过计算用户输入和处理输入后的页面绘制时间的差值来感知延迟时间。这些延迟通常是由于开发人员代码编写不当,引起JS执行时间过长而产生的。
性能指标的计算原理页面性能相关的指标都有了,那么如何采集这些数据呢?
采集页面加载过程的各阶段耗时页面加载过程中的时间点主要依赖NavigationTiming[4]标准,这个标准后来升级到了2.0版本,对应NavigationTiming2[5]标准,两者虽然不尽相同,但是可计算出的指标基本一致。在浏览器中可以通过下面的方式获取:
//navigationtimingconsttiming=window.performance.timing//navigationtiming2performance.getEntriesByType('navigation')基于这些数据,不仅可以计算出DNS/TCP/Request等耗时,还可以计算出DOMReady/DOMParse/Load等耗时。
采集FP&&FCPFP和FCP可以通过浏览器提供的API直接获取,所以采集原理并不复杂。如果页面已经完成了首次绘制和首次内容绘制,可以使用下面的方式直接获取。
window.performance.getEntriesByType('paint')//orwindow.performance.getEntriesByName('first-paint')window.performance.getEntriesByName('first-contentful-paint')但是如果页面还没有开始首次绘制,就需要通过监听获取。
constobserver=newPerformanceObserver(function(list){constperfEntries=list.getEntries();for(constperfEntryofperfEntries){//Processentries//reportbackforanalyticsandmonitoring//...}});//registerobserverforpainttimingnotificationsobserver.observe({entryTypes:["paint"]});采集LCPLCP主要依赖PerformanceObserver,具体监听方式如下:
newPerformanceObserver((entryList)=>{for(constentryofentryList.getEntries()){console.log('LCPcandidate:',entry.startTime,entry);}}).observe({type:'largest-contentful-paint',buffered:true});浏览器会多次报告LCP,而一般真正的LCP是用户交互前最近一次报告的LCP,因为交互往往会改变用户可见的内容,所以用户交互后新报告的LCP不再符合LCP的指标定义。
采集FMP与FP/FCP/LCP相比,FMP的采集相对比较复杂,它需要通过算法计算得出,而业界并没有统一的算法。不过比较认可的一个计算FMP的方式是「认定页面在加载和渲染过程中最大布局变动之后的那个绘制时间即为当前页面的FMP」。由于在页面渲染过程中,「DOM结构变化的时间点」和与之对应的「渲染的时间点」近似相同,所以字节内部计算FMP的方式是:计算出DOM结构变化最剧烈的时间点,即为FMP。具体步骤为:
通过MutationObserver监听每一次页面整体的DOM变化,触发MutationObserver的回调
在回调计算出当前DOM树的分数
在结算时,通过对比得出分数变化最剧烈的时刻,即为FMP
采集TTI&&TBT与FMP相似,浏览器也没有提供直接获取TTI的API,不过针对如何计算TTI有详细的描述,实现对应描述就可以得出TTI的时间点。具体的描述是:先找到FCP的时间点,再往前找到一个安静窗口。安静窗口需要满足三个条件:1)没有Longtask。2)窗口中正在处理的GET请求不超过两个。3)窗口时间窗读应该至少5s。
窗口前的最后一个长任务的结束时间就是TTI。
而通过计算FCP和TTI之间的长任务的阻塞时间的总和,就能得出TBT。
阻塞时间是Longtask中超过50ms后的任务耗时。
采集FID&&MPFIDFID同样依赖PerformanceObserver,具体监听方式如下:
newPerformanceObserver(function(list,obs){constfirstInput=list.getEntries()[0];//Measurethedelaytobeginprocessingthefirstinputevent.constfirstInputDelay=firstInput.processingStart-firstInput.startTime;//Measurethedurationofprocessingthefirstinputevent.//Onlyusewhentheimportanteventhandlingworkisdonesynchronouslyinthehandlers.constfirstInputDuration=firstInput.duration;//Obtainsomeinformationaboutthetargetofthisevent,suchastheid.consttargetId=firstInput.target?firstInput.target.id:'unknown-target';//Processthefirstinputdelayandperhapsitsduration...//Disconnectthisobserversincecallbackisonlytriggeredonce.obs.disconnect();}).observe({type:'first-input',buffered:true});MPFID是FCP之后最长的长任务耗时,可以通过监听FCP之后的Longtasks,对比拿到最长的长任务就是MPFID。
虽然浏览器提供了足够的API来帮助采集各个性能指标,但是在JS中计算具体指标要更为复杂。原因有两点:一是API报告的内容和指标本身的定义有部分差异,所以计算时要处理这些差异;二是部分场景下浏览器不会报告对应内容,这些场景下需要模拟测量。
怎样评估站点整体的性能好坏虽然有众多性能指标,但是每个性能指标评估的都是单一方面,如何整体来看站点的性能是好是坏呢?对于每个单一指标,是否有标准去定义指标的值在具体哪个范围内能算优秀?线上的站点性能应该重点考量哪些性能指标?各个性能指标的权重占多少合适呢?
性能指标基准线Google提供了各个性能指标的基准线,有一定的参考意义。为什么只说是有一定参考意义?首先基准线本身是在变化的,随着指标计算的逐渐更新以及软件硬件的更新,基准线也会有一定的调整。其次用户的使用场景对性能指标也会有很大的影响,比如iOS用户上报的性能指标一般都优于Android用户上报的性能指标。
下方是目前字节内部使用的部分性能指标基准线,基本对齐Google建议的基准线,通过这些数据可以分析站点的性能达标率情况。
衡量站点满意度站点满意度的衡量除了要考虑常规的性能指标外,还要考虑体验类的指标,比如专门衡量视觉稳定性的指标CLS。
线上的站点满意度衡量,一般会在参考lighthouse的满意度计算规则的基础上,去除一些推荐在实验室环境测量的指标的权重。
下方是目前字节使用的线上站点性能满意度的权重计算公式,去除了SI和TBT这两个不推荐在线上环境测量的指标。
那么有了一个站点满意度以后,我们终于能知道一个站点的性能好坏了。那么假设性能不好,我们应该怎样优化?
如何优化站点性能通常,我们可以从性能指标下手去做针对性的优化。虽然指标各不相同,但是优化的思路是相通的。在了解清楚指标的依赖项以后,**通过**优化指标的相关依赖项,就能快速优化性能指标,从而提升站点性能。说起来比较抽象,举个例子:比如当我们想要优化TTI,根据刚刚提到的TTI的计算方式,可以得出TTI的依赖项主要包含FCP、请求和Longtasks,那么尽快的渲染、尽早的请求、请求尽快结束、避免长任务就是优化的关键。关于指标的具体优化措施的内容比较多,将在后续的文章中展开介绍。了解全面的优化措施固然重要,但把每个措施都做一遍并不一定能够高效地解决站点面临的关键性能问题。如何**立竿见影、具有针对性的去优化?通过还原用户加载时的情况来帮助定位性能问题是一个思路。**
利用线上监控定位性能问题一般情况下,前端监控除了监控性能指标以外,还会监控请求和资源的加载以及Longtasks等数据,这些数据可以帮助还原用户的加载现场,帮助找到蛛丝马迹。比如下面这个例子,多项性能指标都很差。通过监控平台还原出的资源加载瀑布图,可以看出绝大部分时间都耗在了拉取资源上。那么就可以初步得出性能优化方案,将优化措施侧重在资源优化上,比如缩小JS文件体积、延迟加载未使用的JS代码等等。
线上监控帮助定位性能问题的例子还有很多,这里不一一介绍了。截图中使用的是字节内部的前端监控平台,目前这套解决方案已同步在火山引擎上,接入即可对Web端真实数据进行实时监控、报警归因、聚类分析和细节定位,解决白屏、性能瓶颈、慢查询等关键问题,欢迎体验。
扫描下方二维码,立即申请免费使用??相关资料[1]Web性能工作组:https://www.w3.org/webperf/
[2]PaintTiming:https://w3c.github.io/paint-timing/
[3]EventTiming:https://w3c.github.io/event-timing/
[4]NavigationTiming:https://www.w3.org/TR/navigation-timing/
[5]NavigationTiming2:https://www.w3.org/TR/navigation-timing-2/