本文来自蚂蚁金服前端技术专家杨森在 ArchSummit 北京 2018 的分享,他将分享如何经过 Performance 相关的 API 准确的采集用户性能数据,并如何经过大数据计算加工最终产出用户性能分析产品,以及如何经过性能数据纵向衡量产品性能、发现性能瓶颈。 本文的主要从如下四个方面阐述: 前端性能监控的两种主要技术方案; 怎么样去作一个真实用户性能数据的采集; 怎么去分析这些数据; 这些性能数据在蚂蚁金服怎么应用。 前端性能监控的两架马车 从技术方面来说,前端性能监控主要分为两种方式,一种叫作合成监控(Synthetic Monitoring,SYN),另外一种是真实用户监控(Real User Monitoring,RUM)。 合成监控 什么叫合成监控?就是在一个模拟场景里,去提交一个须要作性能审计的页面,经过一系列的工具、规则去运行你的页面,提取一些性能指标,得出一个审计报告。 合成监控中最近比较流行的是 Google 的 Lighthouse,下面咱们就以 Lighthouse 为例。前端
Google Lighthouse 系统架构图 Lighthouse 提供了不少种方案,可是咱们这里演示的是一种以命令行工具的形式去对一个淘宝首页作性能审计。咱们能够输出目标页面,它就会在咱们这个模拟环境里面打开淘宝,而后去作一些性能数据的提取。程序员
咱们会看到一个生成的性能报告,从这个性能报告里咱们能够看到,Lighthouse 生成的不只仅是一些性能相关的数据,甚至包括 PWA,而后一些 SEO,甚至一些前端工程化的最佳实践等等。 固然其实业界对于 Lighthouse 也是评价有褒有贬,由于 Google 借助这个看似中立的性能评审工具也是在推行它的一些技术的方案。 合成监控的优缺点 如今,咱们经过下表,来看看合成监控的优缺点:web
真实⽤用户监控(RUM)算法
所谓真实用户监控,就是用户在咱们的页面上访问,访问以后就会产生各类各样的性能指标,咱们在用户访问结束的时候,把这些性能指标上传到咱们的日志服务器上,进行数据的提取清洗加工,最后在咱们的监控平台上进行展现的一个过程。 真实用户监控 (RUM) 的优缺点canvas
由于真实用户监控也是在运行时执行,因此这种真实用户监控比较难采集到一些硬件相关的指标,包括也很难去采集这个页面执行的幻灯片(即逐帧截图)。固然技术上,你能够用 JS 把当前页面保存成一个 Canvas,作一些逐帧对比,甚至把这些数据回传回去。可是在实践过程当中,咱们确定不会这样作,由于这对用户的流量是极大的浪费。介绍完这两种监控方案咱们来看一下他们两种方案的对比。前端工程化
下文中,咱们会着重介绍真实用户性能数据采集的方案。 真实用户性能数据采集方案 我认为,在真实用户性能数据采集时,要关注四个方面的东西: 使用标准的 API; 定义合适的指标; 采集正确的数据; 上报关联的维度。 下面咱们来逐个解析一下。 使用标准的 API 若是你们有研究过前端性能监控,可能会知道浏览器会提供这么一个 API 叫 performance.timing,它会提供一个页面,从开始加载一直到加载完毕,中间各个阶段的一个模型,但这个 API 已经“废弃”了。为何会被废弃?由于 W3C 给咱们提供了更全面、更强大的一个性能分析矩阵,比单一的 performance.timing 更增强大,能帮助咱们从各个方面分析前端页面性能。浏览器
还有 High Resolution Time 这个基础的 API,能够为咱们提供更精准的 timestamp。缓存
为何全部的这些规范都是基于这个高精度时间的规范呢。你们写过 JS 都知道,能够用 Data.API 去提取当前的时间轴,单位是毫秒,这是咱们新的高精度时间的定义,它能够经过 performance.now 来获取,它的单位也是毫秒。可是一样是毫秒, performance.now 返回的变量小数点后面 10 位以上是有的,这里面我列了一个简单的转换式,你们能够换算一下,是能够精确调纳秒级别的。这就是高精度时间的第一个特性。 为何叫高精度时间,就是精度很是之高,也是为了适应如今前端一些复杂的一些性能衡量的场景,包括一些复杂的动画场景,须要的一些高精度的定义。 那么 High Resolution Time 的另一个特性就是,当咱们在用户界面获取当前时间,而后修改一下系统时间,再次调用同一个方法就能够获取当前时间。服务器
为了方便你们去对比,我列了一个辅助线,能够看到,一样是调用 Data.now 这个 API 获取系统时间,若是咱们修改了系统时间,在获取时间的时候,咱们拿到的是,就是当前对应的系统时间。好比说咱们先获取一次,修改系统时间再获取一次,Data.now 就变成了昨天的时间,可是与此同时,HR.time 实际上是不会受这个系统时间变动的影响的,它依然会单调的递增,这个是 HRtime 的第二个特性,它是一个单调递增的。 因此综合来讲,HRtime 有两个特性,一个高精度,一个单调递增,保证了咱们在提取性能指标的时候,不会受宿主环境的一些时间影响。 而后在 HRTime 之上,是一个叫 PerformanceTimeline 的 API,这个 API 很简单,它只是去定义了把各类各样的性能的状况。 在此,咱们得出的最佳实践是:采集性能数据时先抹平 Navigation Timing spec 差别,优先使用 PerformanceTimeline API(在复杂场景,亦可考虑优先使用 PerformanceObserver)。 定义合适的指标 讲完使用标准的 API,咱们来看一看,怎么定义合适的指标,假设有一天你老板问你说,咱们页面性能怎么样,你回答他,反正我以为打开挺快的,这是一个很是不严谨的论述。咱们到底怎么样定义咱们的页面性能呢,其实业界有很是多的方案,这里只是列举了十几个相对来讲比较常见的一些的性能指标定义方式。网络
那么在这么多方案里边,到底哪些指标是适合咱们的,我挑了几个具备典型表明意义的指标例子,给你们作个详细的讲解。 首先咱们来看一看你们最熟悉的页面加载时长,页面加载时长是被清晰的标在这个页面的底部的。它是指 DOM load 事件触发完成,它的优势有: 原生 API; 接受度高; 感知明显(浏览器 Tab 中止 loading)。 缺点是: 没法准确反映页面加载性能; 易受特殊状况影响。 为了解决这个问题,W3C 的工做小组引入了首次渲染 / 首次内容渲染。首次渲染是指第⼀个非网页背景像素渲染,⾸次内容渲染是指第一个⽂本、图像、背景图片或非白色 canvas/SVG 渲染。 这里以打开 YouTube 为例,由于打开这个网站可能相对反应会慢一点,咱们能更容易发现这种区别。当你去采集这两个指标的时候,你会发现,或者说你在大部分时候会发现,这两个指标没有任何差别。 根据咱们实际采集到的性能数据也证明了咱们的观点,在 70% 的状况下,First Paint 和 First Contentful Paint 他们两个指标之间的差别小于一百毫秒,为何?由于如今一个前端网站的架构设计是倾向于单页应用的,它本来的 HTML 结构很是小巧。在渲染的时候,尤为是在第一次渲染的时候,它就已经可以把文本和背景一块儿渲染出来了,这两个指标差别会很是之小。 咱们能够总结一下,这两个指标相比于页面加载时长它更聚焦于页面元素的渲染,相对来讲更客观,但同时能够看到,页面上有象素被渲染出来,并不必定表明着用户去看到了它关心的主要内容,在实际的经验中也能够看到,大多数时候,这两个指标的相差并非特别大。 那么页面加载时长会有异常状况,First Paint 和 First Contentful Paint 又会有各类差别不大,或者是不可以彻底表明这个页面性能的状况,因而就有下面一个算法,经过算法计算出来的指标,叫作 First Meaningful Paint,首次有效渲染时长,这个指标最先是由 Google 提出的,它的一个核心的想法是渲染并不必定表明着用户看到了主要内容,Load 也不必定表明用户看到主要内容,那用户何时可以看到主要内容呢?咱们假设当一个网页的 DOM 结构发生剧烈的变化的时候,就是这个网页主要内容出现的时候,那么在这样的一个时间点上,就是用户看到主要内容的一个时间点。 它的优势是相对校准的估算出内容渲染时间,贴近用户感知。但缺点是无原生 API 支持,算法推导时 DOM 节点不含权重。 而后最后一种指标叫作开始渲染时间,Start Render Time,这个指标时间是没有办法或者很难经过 JS 在用户环境执行来获取的。由于它的定义很是简单,就是假设咱们开始加载页面,一直拿个照相机对着屏幕拍,直到拍到有一帧,发现这个页面不同了,那么这个时间就是页面的一个开始渲染时间。这个定义看起来和那个刚才讲的一个 First Paint 很是像,但实际上,由于它的采集方式不同,First Paint 仍是经过浏览器的渲染引擎来计算出来的时间,而 Start Render Time 就是客观观察到页面的一个加载变化的时间,因此可能 Start Render Time 可以更客观地反映咱们页面的加载状况,可是由于这个指标很难经过 JS 计算,因此是仅仅做为一个参考。 讲了这么多性能指标的定义方法,咱们作一个简单的总结。
这么多的性能指标,咱们到底应该怎么选呢?咱们得出了最佳实践:根据业务特性及性能监控方案选择最适合的性能指标,必要状况下可以使用自定义性能点位。 怎样采集正确的数据? 定义完合适的指标,咱们来看看怎么样采集正确的数据。 不同阶段之间是连续的吗?
这是目前最规范的一个 RUM 性能模型,能够看到咱们的页面加载被定义成了不少个阶段。 不少作性能监控的方案都是计算阶段再上报数据,固然这能够减小数据传输量,可是在实际的应用中咱们会发现,这样作是有问题的。 好比说 DNS 查询完,立刻就开始创建 TCP 链接了吗?TCP 连 接完立刻就开始去发送这个请求了吗?其实是不必定的,除了咱们刚才模型定义的阶段以外,会有一个叫作 stalled 的时间,那么这个阶段发生了什么事情呢?
当你观察到有这种 blocked 或者 stalled 的时间出现的时候,大部分状况下,都是由于被同域名 TCP 并发链接限制了,就好比说,咱们一个页面可能加载了 CDN 上的十个资源,那么在 TCP 链接会有限制,好比 Chrome 它就会有限制。单页面、单个域名的 TCP 链接,并发数是 6,也就是说加载十个资源,只有六个链接被真正创建了,剩下四个资源是要等待这六个链接消费完成以后才能被复用的。 还有其余的一些状况也会致使这个页面 stalled 的时间产生,若是咱们只是用刚才那个性能去计算某一个阶段的时间的话,就会把这个 stalled 的时间漏掉,这样会致使咱们真正把各个阶段加起来去算这个页面总的加载时间的时候,出现一个偏差。在实际观察中咱们会发现,这种 stalled 时间是很是常见的。 每一个阶段都必定会发生吗?
一个最典型的例子,DNS 查询,每次页面访问都会去查 DNS 吗?咱们知道如今浏览器会对 DNS 查询结构作缓存了,你第一次访问了淘宝,你第二次再访问的时候,很大几率 DNS 是不会再查询了的,那么这个阶段可能算出来就是 0,那么在各类特殊的状况下,咱们怎样去采集这个正确的数据呢? 咱们目前得出的结论是:上报页⾯加载开始时间,以及后续各时间点相对增量,在数据端进行阶段清洗和异常处理。 好比说页面开始加载时间是 0 秒,那么从 0 项开始时间是一毫秒,TCP 开始时间是两毫秒,把各个时间点相对的增量算出来。 最后在数据端进行一个阶段的清洗,以及异常的处理,这样一方面可以保证在规范定义中没有被体现出来的阶段不会被遗漏掉,另外一方面也可以让咱们去掌控这个页面加载的分布究竟是怎样的,也不会由于咱们在前端就把这个页面算好,遗漏致使数据的丢失。 采集完合适的指标,最后一个是上报关联的维度。
咱们都知道在作前端的数据采集的时候,维度数据是很是重要的,除了咱们刚才定义的各类度量,怎样采集到合适的相关维度,也可以极大地帮助咱们分析页面性能的效果。 上图提到的点都是必需要采集的,可是在分析页面性能的时候,有不少相对专业的维度是会被你们忽略掉的,好比说当前页面是否可见,这个页面加载方式是怎么样的,它是直接打开,仍是刷新打开,仍是前进后退打开等等。就是经过后面的数据分析,咱们会发现,不一样的页面操做,页面打开方式都会对咱们页面加载的性能会有影响,以及一些更复杂的,好比说是否启用 HTTP二、Service Worker 等等,这些数据咱们都应该尽量采集到,从而可以更好的去分析咱们的页面性能。 准确分析性能数据及影响因素 讲完前端数据的一些采集,咱们来看一看怎么样去分析这些性能数据。这个是咱们采集到真实的蚂蚁的某一个业务的页面加载时长数据。能够看到大部分的页面都会在 10 秒内完成这个页面的加载,可是仔细观察就会发现,还会有大量的长尾值存在,能够看到从 10 秒到 30 秒,甚至到 60 秒,都会采集到这样的数据,这也是一个很是真实的状况。
这个数据能够从两个方面进行解读,第一,因为页面加载时长会受不少异常因素的影响,你的用户可能已经在正常地使用你的网站业务,只不过一个 icon 没加载出来,一个小图片没加载出来,就会致使这个指标变得超长,这都是合理的状况。而另一种状况就是在分析性能数据的时候,咱们会发现,长尾的数据是很是常见的,由于影响外部性能因素太多了,除了前端本身的缘由,除了咱们 JS 写的太大,静态资源太多,没有压缩等等,好比用户的网络很差,服务器压力忽然很高,甚至 IO 堵塞了,均可能致使最后这个页面没有被渲染出来。 在蚂蚁内部,咱们开玩笑说,一些体量比较小的业务,可能有一天它的用户蹲在厕所里面用手机去刷了一下它的网站,页面加载时长统计出来的数据就要暴涨,这都是很是真实可能存在的状况。 咱们理解这些长尾数据以后,来看一看还会有哪些影响咱们这个页面加载性能的因素,我刚才在那个上报维度里面讲到了,页面的可见状态是一个很是大的影响因素,从 Chrome 68 开始,Chrome 启用了一种全新的浏览器的架构模式,在这种模式下,其实它对不可见 Tab 的硬件资源的分配作了一个极大的限制。 你会发现,在你的不可见 Tab 下,可能你的动画会被减慢。那么好比说用户他在后台打开你的页面,就是说它在前一个页面,好比说右键,先打开,后面右键后台先打开,这个时候在你这个新打开的页面去提取性能数据的时候,会发现那个性能就没有正常打开,相对来讲就没有那么好。 另一种页面加载方式也会影响咱们页面性能,好比咱们正常打开一个页面会去加载各类各样的资源,若是刷新打开,可能有一些缓存,浏览器帮咱们作掉了,一些静态资源,304 没有更新,游览器就不会从新下载,好比说前进后退的时候,甚至浏览器会帮咱们作一些页面内容的缓存,有些页面前进后退都是秒出的,根本没有出现那种页面完整刷新的效果,就是这种不一样的页面加载方式,会影响到咱们页面的性能数据的采集结果。 最后一种就是 Service Worker,你们若是实践过就会发现,Service Worker 能力很是强大,它能够作很是强大的缓存,这是可能会影响到整个页面的一个性能。 数据是如何撒谎的? 最后一个我想讲的是,咱们性能数据是怎么样撒谎的,仍是以刚才这个采集到的性能直方图为例,老板问说,你性能采集到了,你给我一个数,大家这个页面性能到底怎么样?我说我算了一下,大概是 6 秒左右。
“我本身是一名从事了5年前端的老程序员,辞职目前在作讲师,今年年初我花了一个月整理了一份最适合2019年学习的web前端干货,从最基础的HTML+CSS+JS到移动端HTML5到各类框架都有整理,送给每一位前端小伙伴,这里是小白汇集地,欢迎初学和进阶中的小伙伴。"
加QQ群:931661106(招募中)
关注公众号:蝌蚪前端
老板不开心了,说这个数据怎么这么大?而后咱们能够经过一些小技巧,一些不一样的加工方式,又算了一下,算出来是 4.2 秒左右,老板说还能够再好一点。又换一种加工方式,2 秒 5 左右,这个数字不错,一样的一份数据,原始的直方图就摆在这里,一样的一份数据,为何会得出三个大相径庭的结论呢? 这是由于咱们指标计算的口径不同,第一个数据,特别大的数据是 95 分位数,所谓 95 分位数就是把咱们全部的页面加载时长,从小到大排列,取第 95% 的位置,这个值做为咱们的性能指标,那么使用 95 分位数,隐含的意义就是,咱们承诺 95% 的人访问咱们页面的时候,页面加载时长是小于 6 秒的。后面一种鸡贼的方法就是咱们取平均数,缓存特别好的,可能几十毫秒就打开了,缓存很差的几十秒才打开,咱们取平均一下,看起来也很中立,4 秒多;若是咱们再鸡贼一点,好比说咱们长尾数据特别长,咱们还能够取 10% 的截尾平均数,掐头去尾取平均,这样的数据看起来就很是漂亮。 可是在实际上,若是咱们作的是一个给本身看的性能产品,咱们应该是对本身的性能数据负责任,因此,在实际分析性能指标的时候,咱们是建议分析性能指标时建议关注百分位数 (percentile),对性能的要求越高,使用越大的百分位数。 好比咱们要承诺 99% 的用户都要小于 5 秒,咱们看页面加载时长时候就应该看 99 分位数。若是咱们如今精力不够,咱们只能承诺 50% 的人页面加载时长小于 5 秒,实际上 50 分位数,就是中位数,就是 50% 的访问可以不小于这个时间打开这个页面。 总结 最后一点,我想讲的是在蚂蚁金服,咱们是怎么用这些性能数据的。 首先性能数据采集固然是要给用户一个分析的结果,由于这是一个性能分析的产品,咱们要从各类维度,各类时间和刚才讲到的关联的维度,给用户一个分析的抓手,告诉它你的性能瓶颈在哪里,你的综合的状况怎么样的,让用户感知他的业务究竟是快仍是慢。 一些客观的数据可以了解本身业务的综合状况,可是看到本身的数据还不够,咱们在蚂蚁金服在作另一件事情,就是对全部中后台产品作一个性能数据的横向比分,固然不只仅是性能数据,这个产品是综合的一个叫体验分的东西,体验包括了性能数据,同时也包括了页面的美观度,用户的感知度,以及一些任务的转化率等等,其中这个性能的分数能够经过咱们采集到的性能的指标来反馈。而后固然你的可能有人就会以为说,咱们业务形态不同,咱们的前端架构方式不同,你把这些分数横向比较有什么意义?
每晚7点直播讲课,送前端学习资料,从基础到框架,专业的老师为你指导
加微❤:QD_666_QD
这个时候就涉及到性能指标的定义,应该尽量的选取对,若是咱们是一个性能监控的中台,而不是一个简单的对本身业务性能的一个监控,咱们应该尽量的选取一个中立的指标去衡量,就是客观的衡量各类业务的一个展现形态,同时在作一个横向比较的时候,咱们也会换用不一样的性能计算方式,好比说在这个体验分项目里面,咱们就会用截尾平均值,目的并非为了让这个数据变得好看,是为了让这个数据变得平稳,让异常值不会产生太大的影响。同时咱们在一些横向比较中,或者在这种大盘的比较项目中,咱们也会尽量的去让用户关注自身性能的一个提高,你能够不关心你比别人快仍是慢,可是你要关心你比以前快了仍是慢了。
以上就是整个性能数据的分享以及在蚂蚁金服的应用,谢谢你们。