[TOC]

真实用户的前端性能监控

之前的文章(web性能分析工具 PageSpeed Insights 和 Lighthouse 使用教程)用到的 PageSpeed Insights 还有 测海外的 WebPagetest,虽然能提供强大的性能分析服务,但作为免费的测试服务,提供的测试样本数量有限,测试结果也不能真正关联真实用户。

如果网站已经过了初创时期,正处在稳步上升的阶段,用户的访问量逐步上升,达到了百万、千万级别,更不可能将网站日常的性能分析和监控,寄望于每日通过 WebPagetest 手动测试得到的样本数量在个位数的性能报告。

所以,如果希望网站能够持续为用户提供最佳的性能体验,就需要 建立网站自身的性能监控系统

准备工作

建立一个基础的真实用户前端性能监控系统,大致包含以下5个系统模块的设计开发工作:

  1. 真实用户前端性能数据采集。 (着重讲)
  2. 采集数据存储。
  3. 监控系统指标定义及加工计算。 (着重讲)
  4. 数据分析、性能报表产出。(着重讲)
  5. 性能基线定义。
    本节着重介绍“1,3,4“

1. 真实用户前端性能数据采集

先确定我们想要得到的数据

1.终端环境数据

  • 设备信息,用于区分PC、Mobile、平板等不同设备。
  • 操作系统。
  • 浏览器。
  • 地区/国家等。
    2.基础性能指标数据

网络相关时间:

  • 页面域名解析时间(DNS Lookup Time)。
  • TCP建立连接时间(TCP Connection Time)。
  • 页面首字节时间,可理解为页面请求等待时间(Time To First Byte)。
  • HTML文档下载时间(HTML Download Time)。
    浏览器端渲染相关时间:
  • 页面开始渲染时间,即白屏等待时间(StartRender Time)。
  • 文档对象模型准备时间(Dom Ready Time)。
  • 页面加载完成时间(Page Load Time)。
    3.页面资源加载详细数据
  • 页面加载包含的IMG、JS、CSS资源的时间消耗数据。

数据采集可行性分析 (哪些数据能拿到,哪些拿不到)

  1. 如何拿到:终端环境数据?
    通常终端环境的数据通过浏览器的DOM API提供的navigator.userAgent属性都可以获取。
    • 这个属性声明了浏览器用于发送HTTP请求的用户代理头的值,它包含的信息如下。Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, likeGecko) Chrome/50.0.2661.102 Safari/537.36
      对应的地区或者国家信息:可以通过解析日志请求中的HTTP Request Header的IP信息,在后期通过相关IP库拿到
  1. 如何拿到:基础性能指标数据?
    对于一些老旧的浏览器,比如 IE6、IE7、IE8等,只能通过在HTML文档中插入各个自定义的占位JavaScript脚本,来模拟获取页面加载过程中浏览器渲染的各个时间,并且获取不到页面加载过程中的网络端的时间消耗。
    • 我们需要通过在文档中不同的位置侵入式地插入脚本来记录各个时间点,过多的侵入式脚本本身也会影响浏览器解析页面的速度。
    • 如果遇到这个问题,可以尝试创建一个项目来专门推动用户升级浏览器
      • 对于用户来说,升级浏览器也意味着页面性能体验的大幅提升,想象一下,同样的页面在IE6和现在非常流行的Chrome浏览器下打开的速度和体验差别,就知道这样做能带来的收益有多大了


此处主要介绍:新版浏览器中已经被支持实现的 HTML5 Performance API来获取更多真实的性能数据
Performance Timing API主要包含3个部分。

  • Navigation Timing:主要提供页面加载过程的性能数据。
  • Resource Timing:主要提供页面所包含的脚本、样式表、图片等资源加载的性能数据。
  • User Timing:方便为在复杂脚本内部记录不同代码片段的执行时间提供API实现。


在这里笔者主要使用Navigation Timing和Resource Timing两个部分的API,通过它们提供的属性数据,基本上可以获取页面从加载开始到结束涉及的网络加载及浏览器端渲染等所有时间消耗。

  1. Navigation API
    window.performance.timing对象中的属性,可以得到当前页面的相关性能信息
    真实用户的前端性能监控 - 图1
    真实用户的前端性能监控 - 图2
    通过对照页面加载时序图,可以清晰地了解页面的不同加载阶段。比如计算域名DNS解析、TCP请求等处理过程时间所需要的属性区间。
    Timimg接口中提供的属性,都是UTC时间
    • 即指定的时间距GMT时间1970-01-01午夜的毫秒数(在下面的介绍中,如果没有特殊说明,默认都是 UTC 时间)。


一般我们都以NavigationStart或者FetchStart的值作为页面加载的开始时间,以ResponseEnd作为分隔点

  - 在 ResponseEnd 之前的时间一般归属到网络相关的消耗
  - 在 ResponseEnd 之后的时间一般归属到终端渲染的消耗,最后以LoadEventEnd作为结束时间。
  1. Resource API
    performance.getEntriesByType(‘resource’)返回数组对象,可以遍历当前页面所包含的资源 (比如js,css,img,xml接口请求 等资源) 加载相关的性能信息,如下:
    真实用户的前端性能监控 - 图3
    通过官方的例子发现,笔者获取某种特定资源的加载相关性能信息也非常方便,如图:
    真实用户的前端性能监控 - 图4

2. 采集数据存储

按情况自行存储

3. 监控系统指标定义及加工计算

从浏览器获取原始的性能数据后,开始对数据进行加工计算,来映射前面定义的和用户体验相关的各种体验指标。

起点 StartTime 的定义:

StartTime:如果NavigationStart不为0,则使用NavigationStart作为页面StartTime基数,如果为0,则降级使用FetchStart,如果FetchStart还是0,则将考虑此数据作废

以下大多可以直接通过 window.performance.timing 获得

目标指标 计算方式 定义
DNS解析时间 domainLookupEnd - domainLookupStart 从发起页面域名解析至完成
TCP建立连接时间 connectEnd - connectStart 从发起TCP连接至三次握手完成
请求等待时间 responseStart - requestStart 从发起页面请求至服务器返回第一个字节
文档下载时间 responseEnd - responseStart 从接收服务器返回第一个字节至主页面下载完成
备注:以上都是各时间段耗时,以下都是total_time,以StartTime为基准
首字节时间 responseStart - StartTime
页面DomReady domContentLoadedEventEnd - StartTime
页面白屏时间 window.chrome.loadTimes().firstPaintTime*1000 - StartTime
页面首屏完成 FirstScreenTime(这个值需要手动埋入代码内, 如下代码展示) - StartTime
页面加载完成时间 loadEventEnd - StartTime

页面首屏的FirstScreenTime手动埋入演示

<html>
  <head>...</head>
  <body>
    <div>..</div>
    <div>..</div>
    <div>..</div>
    <script>
       FirstScreenTime = Date.now(); // 此处已完成首屏展示
    </script>
  </body>
</html

4. 数据分析、性能报表产出

在完成原始指标的加工计算后,后期的数据分析、可视化图表的展示设计就变得非常重要。

  • 多维度的数据分析,加上清晰易懂的可视化图表,能够帮助我们更高效地发现、分析和解决问题

基于经验,下面的维度可以作为常规性能监控系统的常规设计,尤其是当你的网站用户遍布全球时:

  1. 基于不同的设备(区分终端),来自手持设备还是PC等。
  2. 基于不同的国家,不同国家中的不同地区等。
  3. 基于不同类型的用户群体。
    然后基于上面不同维度、不同组合的需求,对数据进行聚合计算,并最终用于图表展示。同样也有可借鉴的常规可视化图表设计:
  4. 性能数据汇总概括。
  5. 变化趋势。
  6. 分布区间、直方图等。
  7. 基于页面的瀑布图绘制(瀑布图绘制可基于最新的Resource Timing提供的数据进行绘制)。

在这里有必要强调一点,对数据进行聚合计算时,通常大家第一时间可能会想到使用平均数来统计计算。

  • 这会导致一个问题,即平均数非常容易受极大值和极小值的影响,如果只看平均数,会导致我们把访问页面非常慢的那部分用户忽略掉。
    • 比如一个用户打开页面用时10s,另一个用户打开页面用时3s,平均耗时为6s。决策者基于这个结果判断出当前页面性能处于还能接受的状态,但实际上打开页面需要10s 的问题被忽略了,无法暴露出来。
      所以推荐大家使用中位数,可以弥补平均数的不足。用中位数来描述一个页面性能数据的集中趋势,然后结合数据区间,来观察不同区间的分布情况,了解页面真实的性能情况。
  • 比如基于自定义的不同数据区间,定义“非常快”“快”“慢”“非常慢”等区间,来观察用户访问性能快慢的分布情况,从而进行针对性的优化。

5. 性能基线定义

按公司需求情况定义


如有误可留言。 如果有用,谢谢点赞~

参考丛书《大型网站性能优化实战:从前端、网络、CDN到后端、大促的全链路性能优化详解》


性能优化合集快速入口: