本文介绍了web性能监控的指标、分类和分析方法,包括合成监控和真实用户监控两种方式。其中,合成监控利用浏览器模拟器加载网页进行性能指标采集,而真实用户监控则收集真实用户的性能数据。同时,文章还介绍了performance API提供的性能监控数据来源和浏览器支持情况。最后,文章提供了不同的方法来计算首屏渲染时间。
或许您已经听说过这样一个问题:“你的Web应用程序性能如何?”作为IT工程师,您会如何回答?您的应用程序是否能胜过大量的Web应用市场?本文将阐述如何进行Web性能监控,包括所需监控的指标、监控的分类、性能分析以及如何进行监控。然而,Web性能监控本身就是一个广泛的话题,在本文中只对其中的一部分进行了研究,某些内容可能不够全面。
前言:为什么需要监控?
web 的性能一定程度上影响了用户留存率,Google DoubleClick 研究表明:如果一个移动端页面加载时长超过 3 秒,用户就会放弃而离开。BBC 发现网页加载时长每增加 1 秒,用户就会流失 10%。
我们希望通过监控来知道 web 应用性能的现状和趋势,找到 web 应用的瓶颈?某次发布后的性能情况怎么样?是否发布后对性能有影响?感知到业务出错的概率?业务的稳定性怎么样?
监控什么?
首先我们需要知道应该监控些什么呢?有哪些具体的指标?
google 开发者提出了一种 RAIL 模型来衡量应用性能,即:Response、Animation、Idle、Load,分别代表着 web 应用生命周期的四个不同方面。并指出最好的性能指标是:100ms 内响应用户输入;动画或者滚动需在 10ms 内产生下一帧;最大化空闲时间;页面加载时长不超过 5 秒。
我们可转化为三个方面来看:响应速度、页面稳定性、外部服务调用
- 响应速度:页面初始访问速度 + 交互响应速度
- 页面稳定性:页面出错率
- 外部服务调用:网络请求访问速度
1. 页面访问速度:白屏、首屏时间、可交互时间
我们来看看 google 开发者针对用户体验,提出的几个性能指标
这几个指标其实都是根据用户体验,提炼出对应的性能指标
1)first paint (FP) and first contentful paint (FCP)
首次渲染、首次有内容的渲染
这两个指标浏览器已经标准化了,从 performance 的 The Paint Timing API 可以获取到,一般来说两个时间相同,但也有情况下两者不同。
2)First meaningful paint and hero element timing
首次有意义的渲染、页面关键元素
我们假设当一个网页的 DOM 结构发生剧烈的变化的时候,就是这个网页主要内容出现的时候,那么在这样的一个时间点上,就是首次有意义的渲染。这个指标浏览器还没有规范,毕竟很难统一一个标准来定义网站的主体内容。
google lighthouse 定义的 first meaningful paint:
https://docs.google.com/document/d/1BR94tJdZLsin5poeet0XoTW60M0SjvOJQttKT-JK8HI/view
3)Time to interactive
可交互时间
4)长任务
浏览器是单线程的,如果长任务过多,那必然会影响着用户响应时长。好的应用需要最大化空闲时间,以保证能最快响应用户的输入。
2. 页面稳定性:页面出错情况
- 资源加载错误
- JS 执行报错
3. 外部服务调用
- CGI 耗时
- CGI 成功率
- CDN 资源耗时
监控的分类?
Web性能监控分为两种主要类型:合成监控(Synthetic Monitoring,SYN)和真实用户监控(Real User Monitoring,RUM)。
一、合成监控
合成监控是一种使用web浏览器模拟器加载网页的方法,通过模拟终端用户可能进行的操作来收集相应的性能指标,并生成一个网站性能报告。一些常见的合成监控工具包括Lighthouse、PageSpeed、WebPageTest、Pingdom、PhantomJS等。
1. Lighthouse
Lighthouse 是 Google 推出的开源自动化工具,其运行方式有两种:一是作为 Chrome 扩展程序运行,另一种是作为命令行工具运行。通过Chrome扩展程序,用户能够更友好地查看报告,并方便地进行阅读。而通过命令行工具,Lighthouse 可以与持续集成系统进行集成。
展示了白屏、首屏、可交互时间等性能指标和 SEO、PWA 等。
腾讯文档移动端官网首页测速结果:
2. PageSpeed
https://developers.google.com/speed/pagespeed/insights/
不仅展示了一些主要的性能指标数据,还给出了部分性能优化建议。
腾讯文档移动端首页测速结果和性能优化建议:
3. WebPageTest
WebPageTest 给出性能测速结果和资源加载的瀑布图。
4. Pingdom
注意:Pingdom 不仅提供合成监控,也提供真实用户监控。
合成监控方式的优缺点:
- 优点:
- 无侵入性。
- 简单快捷。
- 缺点:
- 不是真实的用户访问情况,只是模拟的。
- 没法考虑到登录的情况,对于需要登录的页面就无法监控到。
二、真实用户监控
真实用户监控是一种被动监控技术,是一种应用服务,被监控的 web 应用通过 sdk 等方式接入该服务,将真实的用户访问、交互等性能指标数据收集上报、通过数据清洗加工后形成性能分析报表。例如 FrontJs、oneapm、Datadog 等。
1. oneapm
https://www.oneapm.com/bi/feature.html
功能包括:大盘数据、特征统计、慢加载追踪、访问页面、脚本错误、AJAX、组合分析、报表、告警等。
2. Datadog
https://www.datadoghq.com/rum/
3. FrontJs
https://www.frontjs.com/
功能包括:访问性能、异常监控、报表、趋势等。
- 优点:
- 是真实用户访问情况。
- 可以观察历史性能趋势。
- 有一些额外的功能:报表推送、监控告警等等。
- 缺点:
- 有侵入性,会一定程度上响应 web 性能。
performance 分析
在讲如何监控之前,先来看看浏览器提供的 performance api,这也是性能监控数据的主要来源。
performance 提供高精度的时间戳,精度可达纳秒级别,且不会随操作系统时间设置的影响。
目前市场上的支持情况:主流浏览器都支持,大可放心使用。
基本属性
performance.navigation: 页面是加载还是刷新、发生了多少次重定向
performance.timing: 页面加载的各阶段时长
各阶段的含义:
performance.memory: 基本内存使用情况,Chrome 添加的一个非标准扩展
performance.timeorigin: 性能测量开始时的时间的高精度时间戳
基本方法
performance.getEntries()
通过这个方法可以获取到所有的 performance 实体对象,通过 getEntriesByName 和 getEntriesByType 方法可对所有的 performance 实体对象 进行过滤,返回特定类型的实体。
mark 方法 和 measure 方法的结合可打点计时,获取某个函数执行耗时等。
-
performance.getEntriesByName()
-
performance.getEntriesByType()
-
performance.mark()
-
performance.clearMarks()
-
performance.measure()
-
performance.clearMeasures()
-
performance.now()
…
提供的 API
performance 也提供了多种 API,不同的 API 之间可能会有重叠的部分。
1. PerformanceObserver API
用于检测性能的事件,这个 API 利用了观察者模式。
获取资源信息
监测 TTI
监测 长任务
2. Navigation Timing API
https://www.w3.org/TR/navigation-timing-2/
performance.getEntriesByType(“navigation”);
不同阶段之间是连续的吗? —— 不连续
每个阶段都一定会发生吗?—— 不一定
- 重定向次数:performance.navigation.redirectCount
- 重定向耗时: redirectEnd - redirectStart
- DNS 解析耗时: domainLookupEnd - domainLookupStart
- TCP 连接耗时: connectEnd - connectStart
- SSL 安全连接耗时: connectEnd - secureConnectionStart
- 网络请求耗时 (TTFB): responseStart - requestStart
- 数据传输耗时: responseEnd - responseStart
- DOM 解析耗时: domInteractive - responseEnd
- 资源加载耗时: loadEventStart - domContentLoadedEventEnd
- 首包时间: responseStart - domainLookupStart
- 白屏时间: responseEnd - fetchStart
- 首次可交互时间: domInteractive - fetchStart
- DOM Ready 时间: domContentLoadEventEnd - fetchStart
- 页面完全加载时间: loadEventStart - fetchStart
- http 头部大小: transferSize - encodedBodySize
3. Resource Timing API
https://w3c.github.io/resource-timing/
performance.getEntriesByType(“resource”);
// 某类资源的加载时间,可测量图片、js、css、XHR
resourceListEntries.forEach(resource => {
if (resource.initiatorType == 'img') {
console.info(`Time taken to load ${resource.name}: `, resource.responseEnd - resource.startTime);
}
});
这个数据和 chrome 调式工具里 network 的瀑布图数据是一样的。
4. paint Timing API
https://w3c.github.io/paint-timing/
首屏渲染时间、首次有内容渲染时间
5. User Timing API
https://www.w3.org/TR/user-timing-2/#introduction
主要是利用 mark 和 measure 方法去打点计算某个阶段的耗时,例如某个函数的耗时等。
6. High Resolution Time API
https://w3c.github.io/hr-time/#dom-performance-timeorigin
主要包括 now() 方法和 timeOrigin 属性。
7. Performance Timeline API
https://www.w3.org/TR/performance-timeline-2/#introduction
总结
基于 performance 我们可以测量如下几个方面:
mark、measure、navigation、resource、paint、frame。
let p = window.performance.getEntries();
- 重定向次数:performance.navigation.redirectCount
- JS 资源数量:p.filter(ele => ele.initiatorType === “script”).length
- CSS 资源数量:p.filter(ele => ele.initiatorType === “css”).length
- AJAX 请求数量:p.filter(ele => ele.initiatorType === “xmlhttprequest”).length
- IMG 资源数量:p.filter(ele => ele.initiatorType === “img”).length
- 总资源数量: window.performance.getEntriesByType(“resource”).length
不重复的耗时时段区分:
- 重定向耗时: redirectEnd - redirectStart
- DNS 解析耗时: domainLookupEnd - domainLookupStart
- TCP 连接耗时: connectEnd - connectStart
- SSL 安全连接耗时: connectEnd - secureConnectionStart
- 网络请求耗时 (TTFB): responseStart - requestStart
- HTML 下载耗时:responseEnd - responseStart
- DOM 解析耗时: domInteractive - responseEnd
- 资源加载耗时: loadEventStart - domContentLoadedEventEnd
其他组合分析:
- 白屏时间: domLoading - fetchStart
- 粗略首屏时间: loadEventEnd - fetchStart 或者 domInteractive - fetchStart
- DOM Ready 时间: domContentLoadEventEnd - fetchStart
- 页面完全加载时间: loadEventStart - fetchStart
JS 总加载耗时:
const p = window.performance.getEntries();
let cssR = p.filter(ele => ele.initiatorType === "script");
Math.max(...cssR.map((ele) => ele.responseEnd)) - Math.min(...cssR.map((ele) => ele.startTime));
CSS 总加载耗时:
const p = window.performance.getEntries();
let cssR = p.filter(ele => ele.initiatorType === "css");
Math.max(...cssR.map((ele) => ele.responseEnd)) - Math.min(...cssR.map((ele) => ele.startTime));
如何监控?
在了解了 performance 之后,我们来看看,具体是如何监控的?
总体流程:性能指标收集与数据上报—数据存储—数据聚合—分析展示—告警、报表推送
这里主要讲述如何收集性能数据。
性能指标收集注意项:1)保证数据的准确性 2)尽量不影响应用的性能
1. 基本性能上报
采集数据:将 performance navagation timing 中的所有点都上报,其余的上报内容可参考 performance 分析一节中截取部分上报。例如:白屏时间,JS 和 CSS 总数,以及加载总时长。
其余可参考的上报:是否有缓存?是否启用 gzip 压缩、页面加载方式。
在收集好性能数据后,即可将数据上报。
那选择什么时机上报?
google 开发者推荐的上报方式:
2. 首屏时间计算
我们知道首屏时间是一项重要指标,但是又很难从 performance 中拿到,来看下首屏时间计算主要有哪些方式?
https://web.dev/first-meaningful-paint/
1)用户自定义打点—最准确的方式(只有用户自己最清楚,什么样的时间才算是首屏加载完成)
2)lighthouse 中使用的是 chrome 渲染过程中记录的 trace event
3)可利用 Chrome DevTools Protocol 拿到页面布局节点数目。思想是:获取到当页面具有最大布局变化的时间点
4)aegis 的方法:利用 MutationObserver 接口,监听 document 对象的节点变化。
检查这些变化的节点是否显示在首屏中,若这些节点在首屏中,那当前的时间点即为首屏渲染时间。但是还有首屏内图片的加载时间需要考虑,遍历 performance.getEntries() 拿到的所有图片实体对象,根据图片的初始加载时间和加载完成时间去更新首屏渲染时间。
MutationObserver 接口是 DOM3 Events 规范的一部分,它提供了一种能够监视DOM树更改的机制。
作为一名IT工程师,以下是一种更专业的方式来表达这段文字: 在首屏内容模块中添加一个div,并利用Mutation Observer API监听该div的DOM事件。通过判断该div的高度是否大于0或者大于指定值,确定主要内容是否已经渲染完成。如果渲染完成,则可以计算首屏加载时间。
6)某项专利中描述了一种在加载状态下循环判断当前页面高度是否超过屏幕高度的方法。若页面高度超过屏幕高度,则通过获取当前页面的屏幕图像,并通过逐像素对比来判断页面是否已经完全渲染到屏幕上。
请阅读以下链接以获取详细信息:https://patentimages.storage.googleapis.com/bd/83/3d/f65775c31c7120/CN103324521A.pdf。
3. 异常监控
- 1)js error监听 window.onerror 事件
- 2)promise reject 的异常监听 unhandledrejection 事件
window.addEventListener("unhandledrejection", function (event) { console.warn("WARNING: Unhandled promise rejection. Shame on you! Reason: " + event.reason); });
- 3)资源加载失败window.addEventListener(‘error’)
- 4)网络请求失败重写 window.XMLHttpRequest 和 window.fetch 捕获请求错误
- 5)iframe 异常window.frames[0].onerror
- 6)window.console.error
4. CGI 监控
大致原理:拦截 ajax 请求
数据存储与聚合
一个用户访问,可能会上报几十条数据,每条数据都是多维度的。即:当前访问时间、平台、网络、ip 等。这些一条条的数据都会被存储到数据库中,然后通过数据分析与聚合,提炼出有意义的数据。例如:某日所有用户的平均访问时长、pv 等。
数据统计分析的方法:平均值统计法、百分位数统计法、样本分布统计法。
数据上报
https://blog.csdn.net/qq_41887214/article/details/130659155
- 上报轻量级的数据时可以采取image src属性进行上报
- 特定场景需要采集大量级的数据可以改用普通post请求方式
- 在需要监测用户关闭浏览器时上报数据,首选采用beaconApi方式,若用户的当前浏览器不支持该方法,可降级为image方案。目前很多大厂已采用这种混合式埋点方案。
参考文章
为什么性能如此重要:https://developers.google.cn/web/fundamentals/performance/why-performance-matters
Chrome 中的 First Meaningful Paint:https://juejin.im/entry/598080226fb9a03c5d535cd5
蚂蚁金服:https://www.infoq.cn/article/Dxa8aM44oz*Lukk5Ufhy
FMP:https://docs.google.com/document/d/1BR94tJdZLsin5poeet0XoTW60M0SjvOJQttKT-JK8HI/view#heading=h.k50nnyhtptq0
如何搭建前端监控体系:https://www.zhihu.com/question/37585246
FEX-7 天打造前端性能监控系统:https://fex.baidu.com/blog/2014/05/build-performance-monitor-in-7-days/
首屏时间自动化:https://cloud.tencent.com/developer/article/1061844
精确并自动化地获取页面首屏时间:https://segmentfault.com/a/1190000013532766
如何使用 performance api 来测量性能:https://blog.logrocket.com/how-to-practically-use-performance-api-to-measure-performance/
Improving Performance with the Paint Timing API:https://www.sitepen.com/blog/improving-performance-with-the-paint-timing-api/
chrome-performance 页面性能分析使用教程:https://www.cnblogs.com/ranyonsue/p/9342839.html
阿里云前端监控概述:https://help.aliyun.com/document_detail/58652.html?spm=a2c4g.11186623.6.627.7f782f4dsb9ZV7
first load 与 first meaningful 的区别:https://webenso.com/forget-page-load-time/
其他:https://cdc.tencent.com/2018/09/13/frontend-exception-monitor-research/
lightHouse 实现原理:https://juejin.im/post/5dca05f45188250c643b7d76
Test website performance with Puppeteer:https://michaljanaszek.com/blog/test-website-performance-with-puppeteer
转载自AlloyTeam:http://www.alloyteam.com/2020/01/14184/
本文介绍了web性能监控的相关知识,包括指标、分类和分析方法。合成监控和真实用户监控是常用的监控方式。文章还介绍了performance API的功能和浏览器的支持情况。在计算首屏渲染时间方面,可以使用不同的方法进行监测。数据统计分析是性能监控的重要环节,可以通过平均值统计法、百分位数统计法和样本分布统计法进行分析。对于web应用开发者和系统管理员来说,掌握web性能监控的方法和工具是非常重要的。