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

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 的运行过程、优缺点。

运行过程

在这里插入图片描述
从运行结果页面来看,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 有如下优缺点:
在这里插入图片描述

改进

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

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

首先,提供 SYN 基准运行环境。利用 Lighthouse Node Module 自研 Web 版 SYN 服务并部署在容器中。通过在 Node 服务端添加队列策略,保证单容器任意时间只允许运行一个 SYN 任务,且每个容器的硬件资源( 4 核 + 4G )和网速配置( M 端应用统一使用 10M 网速 )都一样,从而保证运行结果和最终得分是相对公平和可靠的。
在这里插入图片描述

其次,支持以计划任务,间隔 6、12 或 24 小时 的方式运行 SYN 任务。 统计多次运行结果指标的 AVG、TP 值,排除少数异常运行的结果偏差。
在这里插入图片描述

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

在这里插入图片描述

适用场景

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

使用方法

在这里插入图片描述

总结

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 标准废弃,不过当前主流浏览器仍支持,旧浏览器支持好,兼容度高。
在这里插入图片描述
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 等规范,不过兼容性更好、整体性更强。
在这里插入图片描述

优点:

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

缺点:
浏览器兼容不足,尤其在 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 六个指标,效果如下:
在这里插入图片描述

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

其次,我们对 PerformanceNavigationTiming 做了加工处理,效果如下:
在这里插入图片描述
与下面的 W3C 示例图不同:
在这里插入图片描述
原因在于:

  • 实际页面运行过程中,各阶段并不一定如上图那样串行运行。存在 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,判断页面是首次加载还是刷新重载等。
  • 按资源类型,统计各类资源个数、总传输体积和总耗时。
  • 按域名,统计各域名资源个数、总传输体积和总耗时。
    在这里插入图片描述
    能发现慢页面瓶颈点
    我们参考Lighthouse 50 分线 和 HTTP Archive 站点统计数据,根据应用类型是 PC 或 M 来制定慢页面标准,具体阈值如下:
    在这里插入图片描述
    针对慢页面,我们除了采集上节提到的 PerformanceNavigationTiming、web- vitals、小概率的完整 PerformanceEntry 数据和统计数据外,我们还会采集:
  • TOP N 慢资源,做法:取 duration 值最大的 N 个 PerformanceResourceTiming 类型的
    PerformanceEntry。
  • 长任务,指占用 UI 线程大于 50 毫秒的任务。做法:采集所有 PerformanceLongTaskTiming 类型的PerformanceEntry。不过当前大部分浏览器无法提供长任务所在的脚本地址(containerSrc)和方法名称(containerName),采集这部分数据,只能判断长任务是否发生?发生的次数。PerformanceLongTaskTiming内容如下:
    在这里插入图片描述
  • 慢事件,指处理时间超过 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 事件后异步加载的实际情况,我们针对页面是否被正常关闭前提下,总共设置了三种采集时机,其特点如下:
在这里插入图片描述
在这里插入图片描述
为了避免采集指标影响页面性能,我们异步加载 RUM JS SDK,web-vitals 中各指标默认仅支持异步回调,异步加载再异步回调,导致采集时机时仍可能无法拿到各指标值,所以我们对 web-vitals 源码做了改造,支持同步获取各指标值。

上报方式

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

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

ImageXMLHttpRequestsendBeaconFetch
基本原理创建一个 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 实施大纲如下:
在这里插入图片描述

实际编码过程中,具体处理流程如下:
在这里插入图片描述

优缺点

在实施过程中,我们总结 RUM 优缺点如下:
在这里插入图片描述

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

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

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

3 整体架构设计

前文深入分析了 SYN 和 RUM 各自特点、使用方法及优缺点等,我们发现 SYN 和 RUM 各有所长、无法替代,最好同时引用 SYN 和 RUM,构建体系化性能监控架构,提供性能监控和性能分析工具链,支持产研同学在 devpos 各阶段中发现和定位页面性能问题。
在这里插入图片描述
在编码、构建和测试阶段,研发同学可以利用 SYN 来做页面性能测试,判断页面性能是否达标?如果页面性能有问题,再利用 SYN 诊断性能问题,获得优化建议。此举解决前端页面长期以来,前端页面做性能测试难、交付页面质量无标准等痛点。应用部署后,再利用 RUM 采集真实用户页面性能,评估真实页面性能,如果还存在慢页面,仍可以使用 SYN 定位慢页面性能瓶颈点,并利用诊断结果和优化建议,提高优化效率。除此之外,SYN 还可以用来做竞品对比,达到知己知彼、快竞争对手一步的目的。
在这里插入图片描述
有了 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 相辅相成,最能体现页面性能。
在这里插入图片描述

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

输出各级评分

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

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

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

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

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

基于上述两点考虑,我们指标筛选结果和权重占比设置,如下图:
在这里插入图片描述
各指标以其 TP90 统计值,参与评分运算。

选择评分算法
评分算法主要参考 Lighthouse 评分曲线模型,基本原理是:设置两个控制点,通常是 TP50 和 TP90,即得 50 或 90 分时的指标值点,然后根据这两个控制点,生成对数正态曲线,通过该曲线可以获得任一指标值对应的得分。下图是 M 端 LCP 的评分曲线:
在这里插入图片描述

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值 × 指标权重)) / (Σ 权重) ,其中:Σ 表示求和。

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

5 总结

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

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

相关推荐

释产能,论研效提升之路

首先,我们推出了“效能三角指标”策略,通过产能、置信度和健康度三个维度来衡量整个研发过程中的操作规范、流程健康和产能提升情况,例如人均吞吐、需求颗粒度和人均代码当量等维度指标。为快速推进效能提升事宜,在效能委员会之下选出买用业务、二手车数科业务、主机厂新零售三支重点业务作为效能提升实验团队,各个实验团队将在产能、置信、健康不同指标上制定可落地效能提升措施。通过研发过程的可视化,以度量指标为抓手,效能工具平台为基石,文化宣导为护航,构建快速反应并适应变化的可持续专业交付能力,从而提升研发效率。

铸精品·释产能·启未来 9954

智能座机方案建设与实践

在过去的几十年里,通信领域经历了翻天覆地的变化,从早期的模拟信号到数字传输,再到如今的互联网时代,每一次技术的跃进都极大地改变了人们的沟通方式。在20世纪末,固定座机不仅是家庭通信的重要组成部分,也是企业沟通的关键基础设施。随着移动网络的发展,手机逐渐成为人们随身携带的通信设备,尤其是智能手机的普及和发展,彻底改变了通信模式,也对传统通信方式提出了新的挑战。然而,尽管通信技术取得了巨大进步,但在某些方面,固定电话的稳定和可靠性仍是不可替代的。尤其是在特定领域。

铸精品·释产能·启未来 1039

AIGC在广告创意上的实践

本次实践着重于使用AIGC生成小红书文案和汽车类广告创意图片,实践证明AIGC可以有效的降低广告创意的制作周期和成本,在未来拥有广阔的前景。同时也要充分的意识到,目前的实践仅覆盖了广告创意中的一小部分场景,一些对质量要求非常高的场景目前尚无法解决。我们将持续探索出更优的方案,覆盖更多的广告创意场景。

铸精品·释产能·启未来 1787

AI广告创意生产实践

确定创意和概念:首先,确定广告的创意和概念。可以基于品牌价值观、产品特点或目标受众的需要,根据定位和目标,选择适当的场景、主题和情感来传达广告的信息。脚本和故事板编写:根据广告的创意和概念,编写脚本和故事板。脚本用于规划广告中的文字和对白内容,故事板则用于规划广告中每个场景的构图和视觉效果。场景选择和布置:根据广告的概念和故事板,选择合适的场景来拍摄广告。可以在户外道路上、城市街道上,或者在自然风景背景下进行拍摄。根据需要,进行场景布置和装饰,以营造出符合广告要求的环境。

铸精品·释产能·启未来 1228

汽车之家人的“灵创”AI应用平台及编程助手AutoCode

在大语言模型推出后,市场上衍生出了大量的AI编程IDE插件,在调研了Github Copilot、Codearts、Comate、CodeGeeX等市场主流IDE编程插件后,结合汽车之家实际业务场景,云平台上线了汽车之家专属IDE编程插件AutoCode,支持VSCode、JetBrains IDEs、Android Studio,将有效帮助大家解决开发过程中遇到的问题,显著提升开发效率。我们相信,“仓颉灵创”将为公司员工带来更加便捷、高效的办公体验,助力公司业务的持续发展与创新。

铸精品·释产能·启未来 1203

汽车智能化多模态交互

多模态大模型相较于传统大模型使用方式一样,用户只需输入检索的问题,即可获得响应,当多模态大模型解析到用户的意图命中素材库中的图片和视频后,会进行召回展示。在AIGC视频生产方面,多模态大模型可以提供站内新车资讯、热点文章、双车对比文章,服务于内容生产,提升内容质量、解决内容供给不足等问题。基于此,我们研发了多模态大模型,结合了文本、图片、视频等信息,让用户可以轻松可以获取有效信息。可以看出多模态大模型在文本展示的基础上,会增加视频的素材,可以使用户获取信息更直观、高效和具有吸引力。多模态大模型回答结果。

铸精品·释产能·启未来 692

大模型驱动的新范式选车引擎

通过Agent化的方式构建了大模型驱动的生成式搜索引擎GSE,并对其进行了整体评估,在选车场景中传统引擎难以有效应对的长尾问题以及复杂选车问题上,效果独立评估与相对评估(相比传统搜索)上GSE都达到了非常不错的水平,选车体验焕然一新,更加出色。业界实践中,大模型做SFT(Supervised Fine-Tuning,有监督微调)经常会遇到比较棘手的情况,领域上能力增强,但是通用上的能力变得一塌糊涂,各种任务上的评测效果都显著下降,对垂直域上的数据产生过拟合,失去了原有的泛化能力。

铸精品·释产能·启未来 1755

汽车之家质效流水线——奔向未来软件交付的关键里程碑

流水线,即产品发布流程的名词化。根据业务定义的顺序关系,流水线的执行可以定义为依次执行相应的加工或质量校验行为。目前,软件交付全生命周期随着业务的发展不断完善。敏捷概念的提出和应用,大幅提升了软件开发的效率和版本更新的速度,但是它的效果仅限于研发环节,产品发布流程陆续出现新的瓶颈。近年来 CI/CD、DevOps 理念的出现,打破了信息孤岛和沟通壁垒,加深了多岗位人员之间的协作,也加速了软件交付的速度和质量。

铸精品·释产能·启未来 1万+

以终为始,依托度量指标,多种工具全面提升研发效能

经过复杂任务的拆分处理,我们将各个子任务的用例结果存储在内存中。然后,对各个任务的结果进行整合、归类和排重的处理。最终,我们将用例结果进行结构化存储。依靠领域知识库结合任务拆解,我们将大文档分割成小文档,并针对不同的文档类型和用例类型进行多维度的业务知识和技术知识填充。通过这种方式,最大限度地提升了用例生成的准确性和完整度。

铸精品·释产能·启未来 9847

利用生成式AI的产研流程:创新与效率的完美结合

在用例生成的过程中,我们需要考虑多个角度的问题。产研AI整个流程较长,其中文档评分、完善文档、召回文档片段、用例生成都会与LLM进行多次对话,要想利用AI得到预期优质的结果,Prompt 不断的调优至关重要,整个过程根据Prompt设置会经历几轮甚至几十轮对话,这里让我们来通过需求文档生成测试用例来简单介绍产研AI技术实现过程,以下是主要流程。技术领域: 针对不同的专业知识制定了一些标准规范,包括生成用例边界值的设定和性能测试用例的标准,这些规范和知识输出旨在提高AI生成的方式和方法,促进结果准确性提升。

铸精品·释产能·启未来 1万+

浅谈研发数字化在汽车之家的落地实践

综上所述,高效的办公团队对企业的影响是多方面的,从市场竞争到内部运营,从用户满意度到持续创新,都能够为企业带来积极的结果。通过这一度量体系,我们能够更直观地了解和衡量研发团队在交付产品方面的能力,从而不断优化研发流程并控制成本,以提供更多、更好、性价比更高的产品和服务。此外,协作平台还可以集成更多的第三方应用和服务,提供更丰富的功能和工具,满足不同团队的需求。任务拆解完成后,各端同学就可以进行各自任务的“工时评估”和“排期”并更新到系统,然后根据各端“任务”的排期情况,就能得出这个“需求”的整体排期情况;

铸精品·释产能·启未来 1万+

加速未来!汽车之家App应用性能优化总结与后续展望

在当今数字化时代,App已成为人们生活和工作中不可或缺的一部分。客户端性能优化成为App开发的重要任务之一。为了提升用户体验,需要持续关注并改善各项性能指标,如启动速度、页面响应速度、流畅度等。针对这些指标,我们制定了全方位的提升方向,包括工具与平台建设、专项指标体验优化、流程规范等。致力于提升App的稳定性、流畅度和响应速度,为用户带来更好的使用体验。第一时间出现内容(白屏的等待会让用户焦虑),我们在适当的时机和位置添置了骨架屏;提前业务内容出现的时间点(骨架屏只能稍微缓解用户的心情);

铸精品·释产能·启未来 8049

汽车之家APP端到端全链路治理最佳技术实践

在移动互联网和智能化时代,手机App软件已经成为方便工作和生活的重要工具。为了提升用户体验,打造精品汽车消费者阵地,汽车之家对App服务质量问题进行了全方位的专项治理。

铸精品·释产能·启未来 8218

汽车之家铸精品工程

质量对于汽车之家整体业务、用户体验,以及每一个技术人的重要性。

铸精品·释产能·启未来 8190

从汽车之家效能平台建设看汽车行业研发数字化之路

在面向数字化转型过程中,汽车之家逐步深化布局,服务链条覆盖看车、买车、用车多场景,定位从垂直媒体转变为衔接 C 端消费者、B 端主机厂、经销商等各汽车主体的汽车交易及服务平台。

铸精品·释产能·启未来 7826

汽车行业场景化营销新方向:基于 WebGL 的网上虚拟车展

采用三角形网格表示法最大的困难是模型的精确度,即三角面与物体实际表面的差别,高质量的模型往往需要对物体表面进行更多层次的细分,同时伴随着大量的三角形产生,不幸的是这些数据都是需要进行实时传输到客户端的,一般来说Web端一台仅表示外观的3D车模型需要十兆左右的几何数据,仅仅传输这些数据就需要十秒(实测4G带宽)左右的时间,而传统的Web页面的完全展示时间绝大部分情况下不会超过一秒,这种情况会造成用户在进入3D应用时的大量流失。车展作为车市的风向标,代表着汽车发展的趋势,也是厂商展示自己、推广自己的舞台。

铸精品·释产能·启未来 8635

“3D 元宇宙技术”在汽车新零售领域的应用与实践

上海能源空间站是汽车之家用3D元宇宙技术打造的全息舱,现在看到的效果还只是刚开始,我们的研发团队一直都在加班加点对产品功能和体验持续优化和升级,以两周一个迭代的版本速度推进,同时我们还规划了更多的有趣好玩的功能等待大家的发觉。全息舱是我们主机厂产研团队历经10个月的时间从无到有一步步打磨出的一个全新的产品,我们期望能借助我们的技术真正的给业务方和客户方赋能,助力汽车之家迈向新的征程。

铸精品·释产能·启未来 9208

大数据 + VR 全景技术重塑“二手车买车场景”

汽车之家二手车针对二手车车辆数据和视觉展示进行了深度探究,建立了标准化的数据处理加工流程、方法模型以及可视化展示形式。面对海量的复杂的车辆数据,以分治思想建立估值的集成模型,极大提高了估值的准确性,使用户能够准确了解当前车辆的价值;建立标准化的车史知识模型,通过算法模型和规则方法将碰撞、维保、电池的信息结构化,特别是新能源车电池在线检测报告,在业内处于创新领导地位。

铸精品·释产能·启未来 1万+

面向推荐的汽车知识图谱构建

文章主要围绕推荐,介绍了图谱构建详细流程,对其中的困难和挑战做出了分析。同时也综述了很多重要的工作,以及给出了具体的解决方案,思路以及建议。最后介绍了包括知识图谱的应用,特别在推荐领域中冷起、可解释性、召回排序介绍了知识图谱的作用与使用。...

铸精品·释产能·启未来 7576

多模态算法在视频理解中的应用

现阶段视频分类算法,主要聚焦于视频整体的内容理解,给视频整体打上标签,粒度较粗。较少的文章关注时序片段的细粒度理解,同时也从多模态角度分析视频。本文将分享使用多模态网络提高视频理解精度的解决方案,并在youtube-8m 数据集中取得较大提升。实验表明,上述几种方法均有不同程度的提高,尤其以多模态和图卷积提升比较明显。我们希望在未来探索更多的标签依赖关系。GCN 网络也被证明在这项任务中很有用,我们认为值得我们做更多的实验,将 GCN 网络与其他最先进的视频分类网络结合起来。

铸精品·释产能·启未来 5804

基于深度学习的细粒度分类研究及应用

近年来,深度学习的发展极大推动了细粒度分类在交通、医疗、工业、农业、电商等领域的落地。各种反应工业界需求的相关比赛也吸引了大量从业者参加,如专注自然物种分类的iNat Challenge 2021[20]、关于渔业资源保护的Fisheries Monitoring[21]、阿里巴巴主办的AliProducts Challenge等。数据标注:细粒度图像的标注往往需要相关专业知识(如医学等),这给标注带来了极大困难。因此,自监督学习是未来的一大趋势。

铸精品·释产能·启未来 5816

ICDAR 比赛技术分享

ICDAR2021的DIB比赛赛题是对历史文档图像进行二值化,将文字从背景中分离出来。评价指标采用PSNR、DRDM、F-Measure (FM)、pseudoFMeasure (Fps)以及Cohen’s Kappa的综合加权值。比赛难点在于历史文档图像的背景非常复杂,存在各种各样的退化因素,导致现有算法难以取得较好的效果,比如页面污渍挡住字迹,字符褪色,导致和背景太过相似,墨迹浸润,导致背面的文字浸润到正面,但真实的标签又需要把他分到背景里,以及折叠印痕,颜色较深,可能和文字弄混等。

铸精品·释产能·启未来 4351
上一篇: 加速未来!汽车之家App应用性能优化总结与后续展望
下一篇: 浅谈研发数字化在汽车之家的落地实践
之家技术
之家技术 汽车之家技术官方账号 汽车之家技术官方账号
博客等级 码龄4年 459粉丝 · 23原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值