得物技术前端性能监控实践

前言


对于前端来讲,最重要是的体验,而在前端体验中,最为核心的就是性能。秒开率、流畅程度等一系列指标都直接影响用户体验。css

所以,创建一个准确、及时、有效的前端性能监控系统,不只能够量化当前页面的性能水平,还能够为优化方案的效果提供数据支持,此外,还能够在页面性能下滑时提供报警服务,提醒开发人员改善页面性能。前端

监控指标的选取

在参考前人的实践成果后,咱们对性能监控的一系列指标的计算成本,适用性和实用价值进行了评估,认为如下指标和信息是最具实用性和性价比的:python

首先是 fcp(first contentful paint,以下图所示),这个指标是目前统计页面秒考虑的主流指标,虽然它不如 fmp(First Meaningful Paint、lcp(Largest Contentful Paint)、speedIndex 等指标更贴近用户真实使用体验,可是优势是在 Android 端经过调用 Performance API 便可得到,在 iOS 能够经过 raf(requestAnimationFrame)估算,实施过程简单。
web

其次是 tts(time to server),这个指标并无出如今此前看到的文章里边。数据库

它描述的是用户链接到服务器的时间,经过 Performance API 中提供的 requestStart 减去 fetchStart 获得,这个指标不是前端能够经过技术能够优化的,可是它能够反映在当前用户群体的网络环境下,页面秒开率的上限是多少。后端

举个例子,假如经过性能监控数据发现,有 15%的用户访问,须要花费至少 1s 的时间才能链接到咱们的服务器(能够是 SSR 服务器,也能够是 CDN),那么这些用户不管如何都不能秒开,那么此时,某页面秒开率的上限就是 85%。浏览器

假如当前状况下,这个页面的秒开率已经达到了 75%甚至更高,那么继续优化的边际收益会很是低,应该适可而止了。
服务器

第三是 tsp(time for server processing),这个指标也没有出如今此前的参考文章里。微信

它针对的是在使用 SSR 的场景下,服务器内部处理页面请求的耗时,可经过 Performance API 中提供的 responseStart 减去 requestStart 获得。网络

这个环节性能太差,亦会成为拖累秒开率的一个瓶颈,因此必须予以监控;tsp 太长会压榨其余环节的性能预算,过小会增大服务器运维成本。

第四是 css 文件、图片等资源的大小、xhr 请求的持续时间,前两种资源若是不加节制,会致使页面即使作到秒开,也没法快速进入可用状态,好比常见的 feed 流页面。

对于 xhr,须要进行分类讨论,若是是 SSR 页面,则影响不大,只要保障 tsp 处于较低水平,基本上不会拖累秒开率,但若是是 SPA,页面主要功能区都依赖后端数据支持的,好比判断权限、展现 feed 流内容,xhr 的响应速度就很是关键了,也须要予以监控,在指标下滑时,通知后端予以优化和处理。

最后是一些环境信息,好比用户实用什么品牌和型号的手机,是在微信、浏览器、仍是咱们的哪一个版本的得物 App 内打开 H5 页面。

当页面的性能出现问题时,这些辅助信息能帮助咱们尽量精准地复现用户的实用场景,高效、准确地解决问题。

系统架构

整个系统由如下模块构成:

SDK:负责采集用户的页面性能数据和基本信息,按照必定的发送策略将性能数据发往 SLS,植入页面后能够自行采集性能数据,不须要和页面代码交互。

SLS:阿里云日志服务,接受 SDK 发送的数据数据,并为性能数据添加接收时间,ip 等附加信息。

Backend:性能数据后端服务,这个模块有两个功能,一个是定时从 SLS 拉取原始的性能数据,对其进行去重和加工,获得性能指标数据和用户信息数据,而后将这些数据分门别类地存入对应的数据表中,以备查询。另外一个是为数据可视化提供接口数据。

DB:持久化处理后的性能日志数据和性能指标数据的数据库。

Report: 性能数据报表,经过和操做报表,观察特定项目、版本下的指定页面的各项性能指标。

各模块关系以下图所示:

关键技术决策

在进行开发以前,须要对系统的几个关键点进行思考和决策。大体有如下几个点:

  1. 在移动端,unload 事件并非总能触发,因此须要 SDK 可以间歇性地发送数据到 SLS。为了控制发送的频率,同时减小数据的重复量,咱们采起了一种发送间隔逐步延长的策略,即当前页面打开的时间愈来愈长,数据发送的频率会愈来愈低,该页面打开的时间达到必定长度后,SDK 将完全中止工做。
  2. 因为数据存在重复,就须要对用户的端(浏览器、微信、app 内的 webview)进行指纹计算,这里咱们选用了 fingerPrintJS2,在计算时,咱们去掉了致使指纹不稳定的浏览器特征,使用户在打开页面的时候,老是有固定的指纹。可是只依赖指纹是不够的,由于同一个型号的手机,算出来的指纹极可能是同样的。因此,在对性能数据去重的时候,就要同时结合用户的指纹、日志的客户端时间戳、用户设备的 ip 来进行去重,使用同一个 wifi,同一种设备,在同一毫秒打开页面的用户,以目前前端页面的访问量和时间分布来判断。这个去重方案的效果仍是很是好的。
  3. SDK 有一部分同步代码,而且须要在页面载入后尽量及早运行。这意味着若是 SDK 报错,会致使页面没法正常运行,这是很是危险的。因此用 try catch 包裹 SDK 的同步代码,确保 SDK 的异常不会拖累页面。
  4. 某些页面加载的图片、发送的请求会很是多,所有记录加以上报是很是不现实的,因此咱们在监控这部份内容时,只把文件字节数排名前 10 的图片,加载时长排名前 10 的请求的详情记录下来,而后统计一下这个页面加载过多少图片,发送过多少请求。这样既知道页面资源加载的规模,又能找到页面加载最耗时的资源。
  5. 原始日志选择发送到 SLS,主要是由于这个数据发送的并发量很是大,本身作日志服务器成本太高,用 SLS 是一个比较划算的选择。
  6. 后端服务选择用 Python+Django,Python 属于脚本语言,虽然性能差了些,可是对于前端同窗来讲,上手难度会低一些,对于小规模的后端服务,开发效率也能有保障。同时采用 pypy 编译器,能够改善 python 代码的运行速度。此外,经过使用多进程+多线程,能够进一步提升数据的处理速度。
  7. 因为咱们的目的进行统计分析,因此并无必要统计全部的性能数据,为此,咱们采起了等步长采样的方法,即针对一天的数据,咱们每 5%的日志数据,只取前 1000 条进行统计,因为从用户侧上报数据这一行为是随机的,因此这个方案基本上能够保障采样的随机性。这样一来,统计工做的计算量就大幅降低了,保证咱们能够用一个性能很弱的机器就能够完成数据处理工做,也能够保证一旦出现故障,咱们有时间“追回”以前没有处理的数据。
  8. 数据库方面咱们选择了 MySQL,咱们对 IO 并无很是苛刻的需求,因此中规中矩的关系型数据库便可知足需求。

将来发展规划

目前的前端性能监控系统能知足平常的监控需求,可是还能够更进一步:

  1. 对帧率的统计:SDK 目前其实有统计帧率的功能,可是因为原始帧率的数据量过大,因此后期须要转换帧率的统计方式,例如只上报卡顿的时间区间和该区间的帧率分布。
  2. 使用 lcp、fmp 等更科学的指标来替代 fcp。
  3. 额外结合 pageName 去进行去重,进一步提升去重的效果。

文|老狼

关注得物技术,携手走向技术的云端

相关文章
相关标签/搜索