汽车之家页面性能监控建设实践

1 前言

关注用户体验,提高页面性能,是每位前端研发同学的日常工作之一。提高页面性能对业务的帮助,虽不易衡量,但肯定是利远大于弊。如何衡量页面性能优劣?如何帮助研发同学快速定位到页面性能瓶颈点?一直是前端的重点工作之一。本文分享汽车之家在页面性能监控建设方面的部分工作,主要包含三方面:

技术选型

该选择哪些页面性能监控技术方案?
在尽可能不影响页面性能的前提下,既能客观、全面衡量页面性能,又能帮助研发同学快速定位性能瓶颈点,该采集哪些指标?
SPA 应用非首页性能该如何评估?
在尽可能不影响页面性能和保证采集数据精准性的前提下,尽量多地采集和上报数据,如何选择合适的指标采集时机和上报方式?

整体架构设计

整合选中技术方案,构建体系化性能监控架构,提供性能监控和性能分析工具链,支持产研同学在 DevOps 各阶段中发现和定位页面性能问题。

建立评判体系

有数据,我们才能度量;有评分,技术才好改进。

利用采集到的众多指标,根据应用特性,按各性能指标的重要程度,设置不同的基线和权重,以加权平均的方式,求得应用得分。通过分数,直观告诉研发同学应用页面快或慢?应用性能高或低?是否需要改进?

应用得分只能反映单个应用的性能情况,主要服务于产研同学。一家公司有多个部门,每个部门有多个团队,一个团队有多个应用,我们需要公司、部门和团队层级的性能得分,才能让各级领导直观了解其负责队伍的页面性能,也方便上级领导判断下级各队伍之间的性能高低,所以我们根据应用 PV 数和应用级别,仍以加权平均算法,获得团队、部门和公司性能得分。

2 技术选型

根据监控页面性能时的运行环境,我们将技术方案分为两种:合成监控(Synthetic Monitoring,SYN)和真实用户监控(Real User Monitoring,RUM)。

合成监控 (以下简称 SYN)

指在通过仿真环境运行页面,评估页面性能。早期代表工具有我们熟知的 YSlow 和 PageSpeed。随着技术进步,当前三个最成熟的 SYN 工具为:Lighthouse、WebPageTest 和 SiteSpeed。Lighthouse 虽然仅支持 Chrome 浏览器、实施成本较高,但是有谷歌支持、易扩展、指标丰富、有评分诸多优势,已逐步代替 WebPageTest,成为 SYN 首选工具。 如下以 Lighthouse 为例介绍 SYN 的运行过程、优缺点。

运行过程

汽车之家页面性能监控建设实践_第1张图片
从运行结果页面来看,Lighthouse 除了输出关键性能指标值和评分外,还向我们提供优化建议和诊断结果。10.1.0 版 Lighthouse 分别内置 94、16 条性能和最佳实践方面的规范或建议,其中不乏日常研发比少留意且较有意义的规范,如:最大限度地减少主线程工作(mainthread-work-breakdown)、网页已阻止恢复往返缓存(bf-cache)、减少 js 文件中未使用的 JavaScript (unused-javascript)等。

推荐使用 Node Cli 或 Node Module 方式运行 Lighthouse,同时输出 html 和 json 格式的结果。json 中数据更全面,包含如:最大内容渲染时间元素(largest-contentful-paint-element)、应避免出现长时间运行的主线程任务(long-tasks)等明细信息。

优缺点

根据我们实践,总结 Lighthouse 有如下优缺点:
汽车之家页面性能监控建设实践_第2张图片

改进

针对上述不足和产品需求,我们做了一些改进:

  • 为解决 默认无基准环境,相同页面在不同用户端运行,因运行环 境和硬件资源不同,导致结果不同 的问题,我们做了两方面的改进:

首先,提供 SYN 基准运行环境。利用 Lighthouse Node Module 自研 Web 版 SYN 服务并部署在容器中。通过在 Node 服务端添加队列策略,保证单容器任意时间只允许运行一个 SYN 任务,且每个容器的硬件资源( 4 核 + 4G )和网速配置( M 端应用统一使用 10M 网速 )都一样,从而保证运行结果和最终得分是相对公平和可靠的。
汽车之家页面性能监控建设实践_第3张图片

其次,支持以计划任务,间隔 6、12 或 24 小时 的方式运行 SYN 任务。 统计多次运行结果指标的 AVG、TP 值,排除少数异常运行的结果偏差。
汽车之家页面性能监控建设实践_第4张图片

  • 针对 运行慢,占用资源多 的问题。我们认为相同页面,如果没有改版,没必要过于频繁的持续测试,建议 PV 量大或重要页面添加计划任务12 小时及以上时间间隔运行一次,这样既能客观反映页面性能情况,也能节省资源。
  • 将 SYN 集成到 CI 流水线中,将 SYN 作为上线前页面性能测试或竞品对比的实施工具。

汽车之家页面性能监控建设实践_第5张图片

适用场景

  • 将 SYN 作为页面性能测试工具,集成到前端监控后台应用、QA 套件 和 CI 中。建议研发同学交付页面前,通过 SYN 评估页面性能并根据优化建议和诊断结果,改进页面质量缺陷,提高交付质量。
  • 利用 SYN 做竞品对比。
  • 将 SYN 作为分析 RUM 捕获到慢页面的首选工具,结合 Chrome DevTools , 从实践来看可以定位到多数问题。

使用方法

汽车之家页面性能监控建设实践_第6张图片

总结

SYN 实施成本低,便于统一标准,相比 RUM,受运行时环境的影响更小,结果更具有可比性和可复现性,是性能监控的重要一环。基于 Lighthouse,借助 K8S 等容器编排技术,快速搭建提供基准环境的 SYN Web 服务是建设页面性能监控体系的第一阶段。该阶段以提供评估页面性能、分析慢页面等关键功能为主。虽然 Lighthouse 还存在 仅支持谷歌浏览器,代表性不足,也不能真实反映真实用户端的性能情况 这两个问题,但瑕不掩瑜,可以作为 SYN 的首选方案。同时,这两个问题我们将通过添加另外一种技术方案:真实用户监控(RUM)来解决。

真实用户监控 ( 简称 RUM)

顾名思义,指监控运行在真实用户终端(浏览器),采集用户运行页面时候真实的性能指标。业内技术方案主要分为两种:

  1. 依托 W3C 组织且各浏览器厂商广泛支持技术方案:PerformanceTiming 和 PerformanceNavigationTiming。偏向从浏览器处理过程角度去衡量页面运行时各节点、各阶段的耗时。
  2. 各厂根据实际需求自研技术方案。谷歌 web-vitals 是其中最优秀的代表,它从用户体验角度,用更通俗易懂的指标展现页面性能。

除上述两种常见技术方案外,少数商业前端监控服务厂商,除了支持 W3C 和 web-vitals 外,还提供少数自定义性能指标,如:阿里 ARMS 中的 FMP 、字节 WebPro 里的 SPA_LOAD 。SPA_LOAD 用于评估 SPA 非首页的页面性能,有较大创新性,后面还会提及。

技术选型

技术选型主要解决两个问题:1)W3C 的 PerformanceTiming 和 PerformanceNavigationTiming 两规范,应该以哪个为主?2)W3C Timing 规范 和 web-vitals 应该如何协作?
W3C 的 PerformanceTiming 和 PerformanceNavigationTiming 两规范,应该以哪个为主?
PerformanceTiming:已被最新 W3C 标准废弃,不过当前主流浏览器仍支持,旧浏览器支持好,兼容度高。
汽车之家页面性能监控建设实践_第7张图片
PerformanceNavigationTiming:最新标准随 Navigation Timing Level 2 于 2019 年推出,Navigation Timing Level 2 目的是代替涵盖 PerformaceTiming 的 Navigation Timing Level 1。
变更点:
整合 PerformanceTiming 和 PerformanceNavigation 功能。
废弃因各浏览器厂商实现不一,指导意义不足的 domLoading 节点。
添加 ServiceWorker 相关节点。
各属性节点时间使用高精度、以 startTime 为起点的相对时间。
优点:
使用高精度的相对时间,避免因用户端系统时间更改而导致后续节点值不准。
支持 ServiceWorker 相关统计。
缺点:
浏览器兼容性不足。
结论:
从 Can I Use 统计两者兼容度仅差 2.67%,但是从我们实际用户分布来看,使用 PerformanceNavigationTiming,用户兼容度下降 12%,难以接受。所以我们以 PerformanceNavigationTiming 为主,如浏览器不支持,则使用 PerformanceTiming。两者数据格式差异不大,忽略 PerformanceTiming 中 domLoading 节点,使用 PerformanceTiming 时认为浏览器不兼容 workStart 节点即可。
W3C Timing 规范 和 web-vitals 应该如何协作?
PerformanceNavigationTiming ( 以下简称 Timing ): 基于 W3C 规范,着重从浏览器处理过程角度衡量页面性能,以下简称 Timing。
优点:

  • 浏览器兼容性好浏览器兼容性好。
  • 数据丰富,指标全面。即包含各阶段耗时,如:Unload、Redirect、DNS、TCP、SSL、Response、DomContentLoadedEvent 和 LoadEvent;还支持显示页面运行到各节点时的耗时,如:workStart、fetchStart、requestStart、domInteractive 和 domComplate;也可以根据所给节点值,计算 DCL ( DomContentLoaded ) 、window.load 事件或者 PageLoad(页面加载)的耗时。

缺点:

  • 缺乏关键指标。虽然指标多、全,但是不够直观,很难表达用户体验效果。

web-vitals: 目前含 6 个指标:TTFB、FCP、LCP、FID、INP 和 CLS,其中 FID 将被 INP 代替。TTFB、FCP 和 LCP 反映页面加载性能,FID 与 INP 代表页面交互体验,CLS 表示页面视觉稳定性。仅 6 个指标,就能支持对页面加载、交互和视觉稳定方面的评估。不过 web-vitals 部分指标源头还是来自于 W3C 制定的 LargestContentfulPaint、LayoutShift、PerformanceEventTiming 和 PerformancePaintTiming 等规范,不过兼容性更好、整体性更强。
汽车之家页面性能监控建设实践_第8张图片

优点:

  • 指标简明、精练、易懂。
  • 自带基线,能根据指标,判断页面性能优劣。

缺点:
浏览器兼容不足,尤其在 IOS 端。
LCP 可以伪造。做法:给页面添加大尺寸白底图,该图加载时间大概率就是 LCP 值,但是该 LCP 值并没有任何业务意义
受限于 LCP、CLS 原理,对采集指标时机有一定要求,后面会详细介绍。
述求:
真实、客观、全面衡量页面性能。
结论
同时采集 Timing 和 web-vitals 数据,带来好处有:
指标丰富、数据全面,既能利用 Timing 站在浏览器角度反映各节点、各阶段处理耗时,也能通过 web-vitals 直观表达用户视觉体验。建议先通过 web-vitals 直观判断页面性能,再通过 Timing 再进一步分析,达到综合考虑、全面分析,减少因浏览器兼容不足、LCP 造假等情况下,误判页面性能。
结合 Timing 和 web-vitals 数据,更容易定位问题。如:web-vitals 采集到的 TTFB 慢,可以通过 Timing 定位到具体慢在 Unload、Redirect、DNS、TCP、SSL 哪个阶段。
解决 web-vitals 老浏览器兼容不足的问题。如果浏览器不支持 web-vitals, 可以通过 DCL、window.load 事件或者 PageLoad(页面加载)的耗时来判断页面性能。
小节:RUM 技术选型,同时采集 PerformanceNavigationTiming 和 web- vitals。如果浏览器不兼容 PerformanceNavigationTiming,则以 PerformanceTiming 代替。

采集哪些指标

我们的需求是:在尽可能不影响页面性能,既能客观、全面衡量页面性能,又能帮助研发同学快速定位性能瓶颈点。具体包含三方面需求:1) 指标要全面客观;2) 能发现慢页面的瓶颈点;3) 满足前面两需求前提下,尽可能不影响页面性能。

指标要全面客观
首先,我们采集了 web- vitals 六个指标,效果如下:
汽车之家页面性能监控建设实践_第9张图片

谷歌计划于 2024 年用 INP 替换 FID,FID 体现第一次交互的延迟时间,INP 表示所有交互中最长的延迟时间。我们认为 FID 和 INP 都有各自使用场景,同时保留并不矛盾。

其次,我们对 PerformanceNavigationTiming 做了加工处理,效果如下:
汽车之家页面性能监控建设实践_第10张图片
与下面的 W3C 示例图不同:
汽车之家页面性能监控建设实践_第11张图片
原因在于:

  • 实际页面运行过程中,各阶段并不一定如上图那样串行运行。存在 responseEnd 耗时大于 domLoading 的情况。
  • HTTP Cache 阶段并无起止时间节点,只能表明发生在 fetchStart 和 domainLookUpStart 节点之间。
  • ServiceWorkerInit、ServiceWorkerFetchEvent 和 Request
    阶段只有起始节点,没有终止节点,无法统计阶段耗时。对于 Request 阶段不能以 responseStart
    作为终止节点,因为内容在网络中是分帧传送的,不一定可以一次传输整个页面内容。
  • Processing 阶段以 domInteractive 为起点,不符合客观规律,页面执行到 domInteractive 时候 DOM
    is ready,所以 Processing 阶段无法代表页面处理过程。

所以我们结合 W3C 示例图,以点、段和线的方式,展示真实的页面运行过程:

  • 点:指不存在终止节点的节点,含:workStart、fetchStart、requestStart、domInteractive 和
    domComplete,用白色圆点表示。
  • 段:指真实存在起始和终止节点的处理阶段,如 unload 阶段值为:unloadEnd - unloadStart。同理于 redirect、DNS、TCP、SSL、Response、domContentLoadedEvent 和 loadEvent,用蓝色柱状图表示。
  • 线:含页面加载过程中触发的事件,如 DCL ( DomContentLoaded ) 、window.load。另外我们自定义
    PageLoad 事件,表示整个页面加载耗时,值为:loadEventEnd-loadEventStart。用黄色柱状图表示。

段和线具体算法:

  • UnloadEvent = unloadEventEnd - unloadEventStart
  • Redirect = redirectEnd - redirectStart
  • DNS = domainLookupEnd - domainLookupStart
  • TCP = connectEnd - connectStart
  • SSL = connectEnd - secureConnectionStart
  • Response = responseEnd - responseStart
  • loadEvent = loadEventEnd - loadEventStart
  • DCL = domContentLoadedEventStart - startTime
  • WindowLoad = loadEventStart - startTime
  • PageLoad = loadEventEnd - startTime

此外,为了更全面体现页面性能,还采集和统计了如下输入:

  • 小概率 (1%) 的采集完整 PerformanceEntry 数据。PerformanceEntry 包含LargestContentfulPaint、LayoutShift、PerformanceEventTiming、PerformanceLongTaskTiming、PerformanceNavigationTiming、PerformancePaintTiming、PerformanceResourceTiming、PerformanceServerTiming等方面数据,既包含页面本身性能指标,还涵盖资源、网络、缓存、JS 长阻塞任务、慢执行事件等多方面信息,对评估页面性能,判断慢页面瓶颈点很有帮助。考虑到大部分页面内容较多,导致PerformanceEntry 集合条数太大,要是 100%采集上报,对带宽、存储、查询性能都有较大影响,所以只能小概率采集。
  • 页面导航类型,取值于 PerformanceNavigationTiming.type,判断页面是首次加载还是刷新重载等。
  • 按资源类型,统计各类资源个数、总传输体积和总耗时。
  • 按域名,统计各域名资源个数、总传输体积和总耗时。

汽车之家页面性能监控建设实践_第12张图片
能发现慢页面瓶颈点
我们参考Lighthouse 50 分线 和 HTTP Archive 站点统计数据,根据应用类型是 PC 或 M 来制定慢页面标准,具体阈值如下:
汽车之家页面性能监控建设实践_第13张图片
针对慢页面,我们除了采集上节提到的 PerformanceNavigationTiming、web- vitals、小概率的完整 PerformanceEntry 数据和统计数据外,我们还会采集:

  • TOP N 慢资源,做法:取 duration 值最大的 N 个 PerformanceResourceTiming 类型的
    PerformanceEntry。
  • 长任务,指占用 UI 线程大于 50 毫秒的任务。做法:采集所有 PerformanceLongTaskTiming 类型的PerformanceEntry。不过当前大部分浏览器无法提供长任务所在的脚本地址(containerSrc)和方法名称(containerName),采集这部分数据,只能判断长任务是否发生?发生的次数。PerformanceLongTaskTiming内容如下:

汽车之家页面性能监控建设实践_第14张图片

  • 慢事件,指处理时间超过 104ms 的交互事件,做法:采集所有 PerformanceEventTiming 类型的PerformanceEntry。
  • 页面跳转次数,值为:PerformanceNavigationTiming.redirectCount,可以辅助分析 Redirect阶段耗时多的原因。
  • CLS 和 LCP 值大于慢页面阈值时,记录其关联的元素。

不影响页面性能
RUM 必须通过入侵页面,在页面引入 JS SDK 来实现,不可避免地影响页面性能。作为一个发现和分析页面性能的工具,不应该加重页面性能问题。为了将性能影响降到最低,我们做了两方面工作:

  • 异步加载 JS SDK:页面只引入功能单一、体积小的 JS 头文件,待页面到达 DomContentLoaded 事件后,以动态 script 方式异步加载功能完整的 JS 主文件。
  • 减少带宽占用:

    • 抽样上报:慢页面必须上报,非慢页面抽样上报,默认抽烟比例为 30%,减少上报次数。
    • 减少上报数据体积:全量的 PerformanceEntry 数据可以完整体现页面性能,不少页面动辄超过白条 PerformanceEntry 数据,体积过大,所以只能小概率采集全量 PerformanceEntry 数据,计算和采集 PerformanceEntry 的统计数据。

综上所述我们采集指标主要两大类:PerformanceEntry 和 web-vitals。
SPA 应用非首页性能该如何评估?
采集上述指标,已经可以较为客观全面评估常规页面和 SPA 应用首页的页面性能。但是 SPA 应用非首页不是浏览器标准,在 SPA 路由切换过程中:

浏览器仅会执行 History.replaceState()方法,不会也不该重新生成 PerformanceNavigationTiming 数据。
多数 PerformanceEntry 数据包含 SPA 首页以来所有路由切换页面的性能指标。以 PerformanceResourceTiming 为例,无法通过 History.replaceState()方法拿到真实路由切换的时机,排除 PerformanceResourceTiming 集合中的历史数据,从而获取当前路由的 PerformanceResourceTiming 数据。因为各前端框架在 SPA 路由切换过程中,大多数会先执行其框架内部逻辑,而后再触发 History.replaceState()方法,所以 History.replaceState()方法触发时间晚于路由切换的执行时间。
web-vitals 暂时也不支持采集 SPA 非首页路由切换后的性能指标。
所以,对于上述指标 SPA 非首页无法或无法准确拿到,所以我们暂不评估 SPA 非首页性能。

针对业内 SPA 非首页性能难评估的情况,字节 WebPro 创造性地推出 SPA_LOAD 的概念,基本逻辑为:以触发 history.replaceState() 方法为起点,通过 MutationObserver 监听 dom 变更、资源加载、请求发送等变更事件来寻找一个页面达到稳定态的时间为终点,通过计算起终点之间的耗时,来衡量 SPA 非首页的页面性能。SPA_LOAD 类似于常规页面 onload 事件,但是起始时间比真实路由切换时间晚,到终止时间时页面可能已经加载完毕,略有不足,不过已是当前最佳的方案,后面我们可能引入。

采集指标时机

只有采集真实、准确的指标值,才能真实反映页面性能,反之,可能误导产研同学,错误评估真实的页面性能。所以选择采集指标时机有几大原则:

  • 指标准,最关键是要保证指标是准的,不准不如不采。
  • 样本多,上报可靠,部分指标,如 CLS、INP、PerformanceEventTiming
    等,越晚采集值越准,但是越晚采集,留给上报的时间越少,数据上报失败的概率越大,为了尽量多采集数据样本,我们不能等到页面关闭时再采集指标、提交上报。
  • 公平,部分指标,如 CLS、INP、LCP
    等,随着页面打开时间边长,值可能也跟着变大。对于这类指标,我们只能保证数据”样本多“的前提下,选择一个相对合理、对“所有项目”都公平的采集时机。
  • 一次上报,web-vitals
    中部分指标如:LCP、CLS、INP,每次变更都会触发其回调函数,谷歌官方建议每次指标值变更都采集上报。这种处理逻辑,指标值是更准,但是占用太多前端的连接、带宽和
    CPU 资源,也严重加大后端接收程序的处理难度,不是各合理均衡的选择。所以我们要找到一个合适的采集时机,一次采集并上报所有的性能指标。

那么如何确定采集时机?我们得先分析 PerformanceEntry 和 web-vitals 两类指标数据得准确生成时间:

对于 PerformanceEntry 数据,onload 事件触发时,页面已接近加载完毕,PerformanceEntry 中影响首屏加载的绝大部分指标数据已经生成。未生成的数据对评估页面性能影响不大,如:PerformanceNavigationTiming 中的 loadEventEnd 指标值。所以我们认为 onload 事件触发件时,可以采集 PerformanceEntry 指标。
web-vitals 中各指标生成原理不一,onload 事件触发时:

  • TTFB、FCP 指标已生成且不会变,可以采集。
  • LCP 对应的最大元素大概率已加载完毕,所以我们认为这时候 LCP 值大概率是准的,可以采集。
  • CLS 值无法确定是否准确,其计算逻辑为:页面打开后每 5s 作为一个 session 窗口,累加该窗口内产生的偏移值即是 CLS
    值,如果下一个 session 窗口的 CLS 值大于上一个 session 窗口,则替换。所以对于 CLS 指标来说,打开页面 5S
    后、最好 5S 或其整数倍时采集,比较合适,对”所有项目“也比较公平。
  • FID、INP 也无法确定是否准确,它们依赖用户交互操作后才生成,交互操作含:点击、输入、拖放、触摸等事件。FID
    是第一次交互的延迟时间,INP 取多次交互操作中延迟时间最大的值。这两指标都依赖于用户操作,INP
    可能会随着用户操作次数变多而值变大,所以任何时间都没法保证准确拿到这两指标值。

基于上述考虑,我们认为至少要满足:触发 onload 事件或打开页面 5s 之一条件时,才能保证 PerformanceEntry 或部分 web-vitals 指标值准确,采集才有意义。在追求”样本多“的原则下,结合 RUM SDK 是 onload 事件后异步加载的实际情况,我们针对页面是否被正常关闭前提下,总共设置了三种采集时机,其特点如下:
汽车之家页面性能监控建设实践_第15张图片
汽车之家页面性能监控建设实践_第16张图片
为了避免采集指标影响页面性能,我们异步加载 RUM JS SDK,web-vitals 中各指标默认仅支持异步回调,异步加载再异步回调,导致采集时机时仍可能无法拿到各指标值,所以我们对 web-vitals 源码做了改造,支持同步获取各指标值。

上报方式

采集到指标后,需要选择恰当上报方式,将指标可靠的发送到后端。上报方式包含两个部分:上报机制和上报时机。

选择合适的上报机制,首先,要满足功能需求,浏览器兼容度高,对数据大小最好没限制;其次,能感知上报请求异常,便于上报重试,进而提高上报可靠性;最后,客户端支持设置超时时间,避免长时间占用忘了连接,加大后端服务压力。常见上报机制有四种,分别是:Image、XMLHttpRequest、sendBeacon、Fetch API。其特点如下:

Image XMLHttpRequest sendBeacon Fetch
基本原理 创建一个 1 像素、隐藏的 img DOM,将上报地址和上报内容包含在 img 的 src 中。上报成功则返回 200 状态码 利用浏览器内置对象 XMLHttpRequest 上报数据 使用专门设计用于发送分析数据的 navigator.sendBeacon()方法,以异步发送 HTTP POST 请求的方式将分析数据提交到后端 fetch 是一个现代的、基于 Promise 的用于发送 HTTP 请求的 API
浏览器兼容性 中,不支持 IE 中偏低,不支持 IE
数据大小限制 小于 8K 各浏览器大小限制不一,且受制于 CDN、后端代理和 web 服务器,默认值常为 8K。8K 指用 URI 编码后的长度 用 POST 请求,无限制 有,部分浏览器小于 64K 用 POST 请求,无限制
感知上报异常 部分支持。请求返回状态码为 404 或 204 时会触发 img onerror 事件。状态码大于等于 400,不会触发 onerror 事件,如 400、500、502 和 504 等。 支持 不支持。sendBeacon()返回值,只能表示浏览器是否发出请求 支持
可设置超时 不可以,依赖服务端的设置 可以 不可以 可以
优点 使用简单、兼容度高 功能强大,灵活易扩展;无大小限制,兼容度高 使用简单,可靠性高;页面关闭时,仍可发送 相比于 XMLHttpRequest,使用更简介、功能更强大
缺点 数据大小有限制,难感知上报异常 代码编写略复杂,跨域处理要留意 无法感知上报异常,函数返回值 true、false 只能代表是否发送成功 同 XMLHttpRequest 浏览器兼容度最低
适用场景 上报数据小,可靠性要求不高 功能需求多,建议以 POST 方式用 text/plain 或 application/x-www-form-urlencoded 格式上报,CORS 预验证 需要在页面关闭时,上报数据 同 XMLHttpRequest,更适用于浏览器分布较新的终端

上述结论:依赖于 chrome114

相比于 Fetch,XMLHttpRequest 功能几乎一致,兼容性更高;与 Image 对比,XMLHttpRequest 具有兼容度高、数据大小没限制、能感知异常和可设置超时时间等优势;XMLHttpRequest 是页面正常(未关闭)情况下,发送指标数据的首选上报机制。至于 sendBeacon,虽然存在诸多不足,但是页面关闭时仍可以上报数据,成功率还较高,适合作为在页面关闭时,发送尚未发送的指标数据的上报机制。

既然选择了 sendBeacon 作为页面关闭时发送指标数据的上报机制,那该如何判断页面被关闭?传统方案是监听 unload 或 beforeunload 事件,该方案存在两个不足:

不能满足功能需求。手机端用户离开页面时,更习惯将浏览器隐藏,而不是关闭浏览器。隐藏页面时,不会触发 unload 和 beforeunload 事件。
性能有损耗。部分浏览器,监听到 unload 或 beforeunload 事件后,无法使用 bfcache,导致页面性能降低。
更合适、更现代的方案是监听 pagehide 或 visibilitychange===hiden 事件,该方案除了避免传统方案存在的两不足,兼容度也会更高。对于 SPA 项目我们不采集非首页性能指标,触发History.replaceState()方法也算离开页面。

综上所述,整体上报机制为:1)页面正常(未关闭),到采集时机时,SDK 采集性能指标数据,使用 XMLHttpRequest 机制上报;2)通过监听 pagehide 或 visibilitychange===hiden 事件,当页面被关闭时,如果满足任一采集时机条件,则立即采集指标,使用 sendBeacon 机制上报。

整体方案

综上所述,我们整理 RUM 实施大纲如下:
汽车之家页面性能监控建设实践_第17张图片

实际编码过程中,具体处理流程如下:
汽车之家页面性能监控建设实践_第18张图片

优缺点

在实施过程中,我们总结 RUM 优缺点如下:
汽车之家页面性能监控建设实践_第19张图片

RUM 架构设计复杂,实施成本较高,由于是技术刚需,只能投入资源,努力做好。

针对 无诊断结果和优化建议 的缺点,可以结合 SYN,取长补短,利用 SYN 诊断慢页面性能瓶颈点分布。

对于 无评分,没法判断页面性能优劣 的问题,我们分三步走:首先,制定慢页面标准,判断单次页面是否快慢,标准值前文已有描述;其次,统计该页面各重要指标的 AVG、TP50、TP90、TP99 值,全面评估页面所有请求的性能分布;最后,我们会对页面所在的应用进行评分,直接告诉研发同学,该应用性能优劣,应用评分具体做法,会在下面《建立评分体系》章节细讲。

3 整体架构设计

前文深入分析了 SYN 和 RUM 各自特点、使用方法及优缺点等,我们发现 SYN 和 RUM 各有所长、无法替代,最好同时引用 SYN 和 RUM,构建体系化性能监控架构,提供性能监控和性能分析工具链,支持产研同学在 devpos 各阶段中发现和定位页面性能问题。
汽车之家页面性能监控建设实践_第20张图片
在编码、构建和测试阶段,研发同学可以利用 SYN 来做页面性能测试,判断页面性能是否达标?如果页面性能有问题,再利用 SYN 诊断性能问题,获得优化建议。此举解决前端页面长期以来,前端页面做性能测试难、交付页面质量无标准等痛点。应用部署后,再利用 RUM 采集真实用户页面性能,评估真实页面性能,如果还存在慢页面,仍可以使用 SYN 定位慢页面性能瓶颈点,并利用诊断结果和优化建议,提高优化效率。除此之外,SYN 还可以用来做竞品对比,达到知己知彼、快竞争对手一步的目的。
汽车之家页面性能监控建设实践_第21张图片
有了 SYN 和 RUM,我们可以构建在线持续优化慢页面的闭环,如上图。RUM 负责采集真实用户产生的慢页面,经后台存储、聚合,自动将 TOPN 的慢页面创建 SYN JOB,待 JOB 运行完毕,将诊断结果和优化建议以告警防止通知研发同学,研发同学利用 SYN 优化建议,再利用 devtools、webpack 等工具,改进页面,交付高质量页面,降低慢页面的频次。如此循环迭代,持续优化应用页面性能,最终应用能达到极致性能。

4 建立评判体系

引入 SYN,自研 RUM SDK,采集到众多 SYN 和 RUM 指标数据后,我们将着手建立评判体系,评估各应用、团队、部门,乃至整个公司的性能情况,输出少数几个关键指标和评分,直观告诉各层级、各角色员工其所在组织及同级组织的页面性能情况,通过得分和同级对比,评判是否要优化页面性能?

建立评判体系时我们秉持着:既要突出重点、关键指标,同时还能全面、综合、客观真实地反映页面性能 的原则。所以我们将评判体系分为两大块,其一,展示最关键的性能指标。其二,输出各指标、应用、团队、部门及公司层级的性能评分和层级。

展示最关键的性能指标

我们从 web-vitals 和 performanceNavigationTiming 中各选一个最能代表页面性能的指标,分别为:DCL 和 LCP。LCP 兼容度不高且可能被伪造,DCL 既能代替 LCP 部分反映首屏性能且兼容度高、难以伪造,和 LCP 相辅相成,最能体现页面性能。
汽车之家页面性能监控建设实践_第22张图片

TP90 代表 90%用户的体验下限,与 AVG、TP50、TP75 比,覆盖和统计更广的用户,还能屏蔽页面在手机端特殊网络环境下,产生的少数脏数据,更具有代表性。

输出各级评分

应用性能评分
为了全面、综合和客观的评估应用页面性能。我们将选择各维度具有代表性指标,参考 HTTP Archive 给予的业内指标分布,根据应用特性,如应用类型( PC 或 M 端 )、终端用户( C 端用户、B 端客户和内部员工),设立不同的评分基线,算出各指标得分。再根据各指标重要程度设置权重,通过加权平均算法,求得应用性能评分。流程如下:
汽车之家页面性能监控建设实践_第23张图片

获取应用性能分过程中涉及几个重要流程:1) 指标筛选及权重设置;2) 选择评分算法;3) 确立指标基线;4) 使用加权平均算法计算应用性能分。下面逐一介绍。

指标筛选及权重设立
此过程中,我主要考虑两点:

全面综合考虑。页面性能涵盖多方面,传统上只用首字节、白屏、首屏时间等少数指标来衡量,较为片面,不够客观。我们认为评判页面性能应该涵盖各维度指标,如:页面加载、交互体验和视觉体验。此外我们还引入慢 API 比例的概念,API 请求比例指页面打开后 API 耗时超过 3s 的请求比例,慢 API,即可能影响首屏加载耗时,也会影响交互过程中的用户体验。为了让研发同学关注慢页面、将在线 SYN 作为日常开发性能评估工具,我们将 SYN 评分也作为权重指标,后台系统每天会统计访问次数 TOP10 的慢页面并自动创建 SYN 定时任务,待任务执行、分析完毕后,将优化建议和诊断结果通知研发同学。SYN 评分项,包含性能分和最佳实践分,两者都是百分制。

突出重点。提高重要指标的权重比。如:加载指标用于衡量页面能不能用,最为关键,所以赋予权重占比最大。LCP 是最重要的加载指标,权重占比也相应提高。由于 LCP 本身不一定完全合理且可能被伪造,所以评判页面加载性能时,还引入 DCL、FCP、TTFB、WindwLoad 和 PageLoad 等加载指标。该做法,优点:指标多,维度广、角度大、更全面和更客观准确;缺点:增加评判系统复杂度和难度。

基于上述两点考虑,我们指标筛选结果和权重占比设置,如下图:
汽车之家页面性能监控建设实践_第24张图片
各指标以其 TP90 统计值,参与评分运算。

选择评分算法
评分算法主要参考 Lighthouse 评分曲线模型,基本原理是:设置两个控制点,通常是 TP50 和 TP90,即得 50 或 90 分时的指标值点,然后根据这两个控制点,生成对数正态曲线,通过该曲线可以获得任一指标值对应的得分。下图是 M 端 LCP 的评分曲线:
汽车之家页面性能监控建设实践_第25张图片

m 表示中位数,图中 m 值为 4000ms,表示当 LCP 值为 4000ms 时,得 50 分;同理 p10 为 2520ms,LCP 值为 2529ms 时,得 90 分。设置 m 和 p10 后,会生成右边的评分曲线模型,根据该模型可以获得 LCP 值从 0 到正无穷时的得分。

确立指标基线
确立指标基线是为了给评分算法提供两个控制点,即 m 和 p10 的值。确立方法有三种:

  • 直接借用 Lighthouse 配置。以 Lighthouse 对应指标的 50 分和 90 分阈值为 m 和 p10 控制点值,如
    web-vitals 中各指标。
  • 参考 HTTP Archive 统计数据,以统计数据中的 p10、p75 值为 m 和 p10 控制点值。如
    performanceNavigationTiming 各指标。
  • 自建基线。少数指标基线无法从上述两种方法确立,只能自建基线。具体做法:以当前后台系统拿到的数据为样本,以样本的 tp75 和 tp95
    值为 m 和 p10 控制点值。适用于慢 api 比例指标。

确立基线过程中,应考虑应用价值和研发要求的不同,根据应用特性,有针对性的设置。首先,PC 端和 M 端类型的应用,要设置不同的指标基线,所幸 Lighthouse 和 HTTP Archive 都提供 PC 端和 M 端的配置参考;其次,根据应用实际终端用户类型,有针对性的调整阈值。C 端和 B 端应用,直接产生业务价值,性能要求要比内部应用高,两控制点值应该更低。

使用加权平均算法计算应用性能分
根据指标 tp90 值、指标基线和评分算法,求得该指标的百分制得分。
使用加权平均算法求应用性能分,结果 = (Σ (指标tp90值 × 指标权重)) / (Σ 权重) ,其中:Σ 表示求和。

各级组织评分
至于求各级组织,含团队、部门及公司的性能分,则根据其管辖的应用个数、应用性能分及应用权重,仍使用加权平均算法获取组织分。流程如下:
汽车之家页面性能监控建设实践_第26张图片
求组织性能分的难点是:如何设置应用权重?我们主要参考应用的 PV 区间和应用级别。PV 层级越大、应用级别越高,权重越大。具体配置参考如下:
汽车之家页面性能监控建设实践_第27张图片
PV区间中的PV 值指采用 PV,而非真实 PV,采用 PV=采集到 RUM 数据量/抽样比例。
经过上述步骤,我们能获得应用性能分,以及各级组织的性能分,如团队性能分、部门性能分及之家性能分,展示效果如下:
汽车之家页面性能监控建设实践_第28张图片
性能分参考lighthouse标准,50分以上算合格,90分以上才算优秀。

5 总结

通过页面性能监控和评判体系建设,我们既有原始页面性能数据,又有聚合统计值,还有最终评分。通过评分、统计和原始数据,打通了发现、定位和分析性能问题的链路。研发同学可以通过评分直观判断应用性能优劣是否需要优化?如果需要优化,再通过聚合统计数据,分析应用瓶颈点;定位具体瓶颈点时,可以查看明细数据,辅助分析产生瓶颈点的具体原因;改进后可以通过通过统计查看优化效果,最终反映到提高评分上。

受限于篇幅,本文仅能介绍页面性能监控和评判体系建设相关的实践。一个完整的页面性能监控系统,还应该包含:监控、报警、优化、治理等模块,不仅仅只是指标,能度量页面性能,发现其中性能瓶颈点,帮助研发同学提升优化效率,还要治本,通过构建一系列前端工具链,改进交付过程,通过规范、工具和流程,从源头上提高页面交付质量,避免将问题带到线上,先于用户发现性能问题。
参考文献
vitals-spa-faq
Web Vital Metrics for Single Page Applications
Beaconing In Practice
HTTP Archive
一个收集和分析网站性能数据的项目,旨在帮助 Web 开发者了解互联网的技术趋势以及性能优化的最佳实践。

你可能感兴趣的:(汽车之家页面性能监控建设实践)