全球即时看!转转B端项目页面性能统计实践
由于转转前端业务方向主要偏向于 C 端,比如 App端内 H5、 小程序内 H5 等,并且技术栈以 Hybrid 为主(承载容器为转转标准化webview)。但是,近些年随着业务不断扩大,逐渐出现了如乾数据平台、行星平台等 专门服务 B 端的FE项目。但是没有相关性能数据来作为参考支撑,比如需要分析用户体验质量;分析现有页面性能缺陷以及后续需要做性能优化的方向等。因此,需要一款符合转转内部埋点上报体系的 PC 端项目网页的性能统计平台。
B 端性能统计面临的问题由于内部性能埋点统计体系不支持分批/分段上报,每个 Router 都需要作为一个单独的页面进行一次性的性能数据上报。在 B 端,一些新的指标需要支持和特殊处理。因此,在数据采集统计方面,我们会遇到以下几个问题。
SPA Router 问题 转转内部 C 端项目主要采用 hybrid 技术栈,因此不需要对 SPA 项目路由做特殊处理(因为每次都开启一个 webview,类似于多页面应用应用场景)。但是,基于 React 技术栈的 B 端项目是 SPA 项目,为了方便统计每个 Router 页面的性能数据,我们需要对每个 Router 页面的加载进行一些特殊处理。SPA 资源统计问题 现在的前端 SPA 项目一般都会通过异步加载页面资源的方式,进行页面打包体积的优化,以提升页面首屏性能。因此,在进行资源统计时,我们需要单独对相应的 Router 页面的加载资源进行统计处理。B 端指标定义问题 转转 B 端性能统计主要参考核心指标:白屏、首屏、完全加载。页面性能分数评估也主要基于这三个指标进行加权计算。但是,在 Router 页面加载时,我们会遇到核心性能指标无法直接获取的问题,因为 Router 切换并不会产生页面的 load,而只是 div 的显示隐藏。当然了,还需要其他 B 端特有的业务标识定义,这里不一一列举。主要内容1. 性能指标定义定义好哪些性能指标需要上报,是做好一个完善的采集性能数据采集 sdk 的前提条件,经过分析主要将指标分为两类:1. 纯 H5 页面性能指标 2. 页面相关业务性指标。
(资料图片)
以上提到的绝大部分指标,可以通过浏览器提供的PerformanceNavigationTimingPerformanceResourceTimingAPI 和 谷歌团队提供的web-vitals工具函数很方便的进行获取和计算。
业务性相关指标:所谓业务性指标,主要是作为查询分析的一些要素,比如 我们想查询某个业务线的某个项目的某个页面在某个平台下某个性能指标的表现如何?那么就需要一些非页面性能本身的业务要素指标进行定义和上报统计。
业务指标主要包括:actiontype埋点类型标识 、pagetype业务线/项目标识、pageid页面标识 、clientType端信息、pagestate页面状态、pageurl页面url、cookieid用户id、fromType来源、loadcnt加载次数 等等。
2. 指标数据的获取与上报PS: web-vitals 由于在苹果和低版本安卓的兼容性存在问题,因此没有在 C 端作为一个必选项,但 B 端用户绝大多数使用 chromium 内核浏览器,所以大胆的将 web-vitals 纳入采集指标中
上面进行了各种指标的定义,那么如何高效有序的接入到转转埋点体系内进行上报统计呢?转转内部其实已经有了 C 端埋点体系,其实只需要按照一定的规则进行接入即可,主要是性能平台B端项目需要的字段和后端已有日志表结构做好关系映射和扩展。
为了解决上面提到B端项目的特有问题,以及满足上述提到所有性能指标、业务指标都可以很优雅的进行上报统计,方便在代码层面更好的进行结构上的解耦,并且尽量做到性能计算统计相关程序不影响页面本身的性能,在技术实现设计层我们把上面的指标做了一些分类,比如 同步计算指标(基础业务同步指标、基础性能资源同步指标)、异步计算指标(性能异步指标、后置异步指标)等。具体如下图所示。
技术层面指标分类
下面详细介绍一下一些关键逻辑是怎么处理的?各类性能指标具体是怎么计算的?下面列出了部分指标怎么获取和计算的关键代码。
SPA 项目的路由页面的拦截关键逻辑:
const hackRouter = () => { if (!window?.history?.pushState) { return; } // 浏览器的历史记录发生变化时被触发, 导航前进、后退 const oldOnPopState = window.onpopstate; window.onpopstate = function(this: WindowEventHandlers, ...args: any[]): any { const to = window.location.href; const from = lastHref; lastHref = to; // 通知订阅的回调 triggerHandlers("history", { from, to }); if (oldOnPopState) { try { return oldOnPopState.apply(this, args); } catch (e) {} } }; // history pushState 或 replaceState 触发,通过 history api 方式 const wrapHistoryFn = (type: "pushState"|"replaceState") => { const originalHistoryFunction = window.history[type] return function(this: History, ...args: any[]): void { const url = args.length > 2 ? args[2] : undefined; if (url) { // coerce to string (this is what pushState does) const from = lastHref; const to = String(url); lastHref = to; // 通知订阅的回调 triggerHandlers("history", { from, to }); } return originalHistoryFunction.apply(this, args); }; }; window.history.pushState = wrapHistoryFn("pushState"); window.history.replaceState = wrapHistoryFn("replaceState");}
性能基础指标的获取相关代码:
// 获取 PerformanceTiming 相关数据export const getPerformanceTimingData = (task: TaskTypes) => { if (!window?.performance?.timing) return {} const { metrics } = task; const { state } = task.ctx; const ptiming = performance.timing; // 默认为 -1 方便过滤无效值 const result = { blankTime: -1, dnsTime: -1, httpTime: -1, domTime: -1, domReady: -1, // ... } // 页面加载状态 if(state === "pageload") { // ... // 白屏 result.blankTime = fix(ptiming.responseStart - ptiming.navigationStart); // DNS查询 result.dnsTime = fix(ptiming.domainLookupEnd - ptiming.domainLookupStart); // HTTP请求 result.httpTime = fix(ptiming.responseEnd - ptiming.responseStart); // 解析dom树 result.domTime = fix(ptiming.domComplete - ptiming.domInteractive); // DOMready result.domReady = fix(ptiming.domContentLoadedEventEnd - ptiming.navigationStart) // ... } // 路由切换状态 if (state === "navigation") { // ... } return result}
资源相关指标的数据获取关键逻辑:
// 记录let performanceCursor: number = 0;// 获取当前页面资源列表export const startPerformance = (task: TaskTypes) => { const { timeOrigin } = task.ctx; if (!window.performance || !window.performance.getEntries || !timeOrigin) { return; } // performanceEntries const performanceEntries = performance.getEntries(); const pss = performanceEntries.slice(performanceCursor); // 处理 各种 performanceEntry 资源 formatResourceEntries(task, pss); performanceCursor = Math.max(performanceEntries.length - 1, 0);}export const formatResourceEntries = (task: TaskTypes, entries: PerformanceEntryList) => { const { state, startTimestamp, timeOrigin } = task.ctx; const { metrics } = task entries.forEach(entry => { const startTime = entry.startTime; // console.log( timeOrigin, startTime, startTimestamp, timeOrigin + startTime < startTimestamp) if (state === "navigation" && timeOrigin + startTime < startTimestamp) { return; } const baseStartTime = startTimestamp - timeOrigin; switch (entry.entryType) { case "navigation": // 处理 bodysize // ... case "paint": // 处理 paint 指标 fcp fp // ... case "resource": // 序列化各种资源, 如js/css/img/jsonp/ajax/fetch/iframe... calcResource(entry, result, baseStartTime); } // ...}
业务指标数据的获取:
// 初始化基础业务指标export const initBaseData = (task: TaskTypes) => { const { params = { backup: {} }, options = {} } = task; // ... Object.assign(params, { pagetype: options?.pagetype || pagetype, actiontype: options?.actiontype || actiontype, appid: options?.appid || appid, // and more ... }); return task;}
longTask 的记录获取:
function startLongTasks(): void { const entryHandler = (entries: PerformanceEntry[]): void => { for (const entry of entries) { const startTime = entry.startTime const duration = entry.duration; const endTime = startTime + duration; const longtask = { name: `longtask-${++n}`, startTime, endTime, duration } longTasks.push(longtask); } }; if(PerformanceObserver?.supportedEntryTypes?.includes("longtask")) { // 注册 longtask 异步任务 observe("longtask", entryHandler); }}
在实际项目统计时,发现一些性能指标算法的适用性问题需要注意:
LCP 算法存在的问题。比如:触发条件限制的问题,当检测到用户输入时候 FMP算法会停止计算,就导致某些场景触发不了(比如主要内容还没显示就点击页面)。白屏占位图问题,页面初始有较大的白屏占位图时 即使后面被移除了,LCP 算法还会把它当作主要内容。FMP 算法不适合某些特殊场景。比如:2/3 是金刚位图片布局,最下面 1/3 区域有一个瀑布流,由于FMP算法计算规则会导致统计时间在瀑布流请求之后展现后,就导致直观上的页面首屏时间变大。
数据可以计算并获取了,那么如何进行友好的处理上报?
由于内部埋点提下不支持回话形式的分段上报,那么就需要在前端提前准备好所有需要需要上报的数据的处理,整体B端 SPA 项目性能数据处理的上报处理机制,以及同步任务数据、异步任务数据任务的处理流如下图所示。
3. 上报数据的体积优化在进行数据上报时,如果页面的静态资源加载 / ajax请求数量很多时,埋点上报请求接口的 body 会很大,导致请求耗时长而影响页面本身的性能。因此针对 body 过大的问题,对一些资源的统计做了序列化处理。
比如:单条静态资源的原始数据结构为:
const entry:PerformanceResourceTiming = { "name": "https://xxx.zzz.com/yyy.css?v=5J1NDtbnnIr2Rc2SdhEMlMxD4l9Eydj88B31E7_NhS4", "entryType": "resource", "startTime": 1924.6000000238419, "duration": 1400.5999999642372, "initiatorType": "link", "fetchStart": 1924.6000000238419, "responseEnd": 3325.199999988079,}
序列化之后,将各个关键数据合并成一个字符串,即:
// 将 entries 分类,并把单个entry 进行字符串化后,再将所有 css entry 合并const cssEntry:string = "https://xxx.zzz.com/yyy.css|1924|1924|3325"
可以发现系列化精简后将 255个字符优化成了 42 个字符。
4. 数据存储与处理往往B端 SPA 项目静态资源和请求多达几十上百个,这样序列化处理合并之后,能将埋点上报请求 body 体积减少数千个字节。当然了,如果服务支持编解码,还可以通过其他更优的序列化方案进行 body 体积压缩。
在对数据进行处理时,也遇到了一些问题。
每天上报的性能埋点数据存储在哪里?
如何计算数据?如何扩展数据?如何查询数据?
二次计算后的数据量依旧非常大,该怎么办?
原始性能数据通过SDK采集后,经过数据仓库的清洗,存储在Hadoop中。虽然Hadoop可以存储PB级别的数据,但查询速度较慢,不适合实时性能分析查询。为了解决这个问题,我们尝试将清洗后的数据复制一份存储在MySQL中,但随着数据量的增加,MySQL方案出现了许多问题。考虑到实际场景的并发量不会太高,我们最终选择将明细数据存储在ClickHouse中。虽然明细数据可以通过查询ClickHouse获取,但对于许多聚合计算得出的数据,如果仍然通过查询并实时计算,效率并不理想。因此,在数据落库后,我们通过定时任务预先计算一部分聚合数据,然后将其导入MySQL中。这种做法的好处在于,这部分预先计算好的数据可以进行查询,用户体验更好,而且后续需要扩展时,只需要对聚合数据进行二次计算加工即可。目前,聚合数据使用了6个维度进行分组计算,这些维度也可以用于组合查询,方便后续扩展。尽管聚合数据已经合并计算过,但由于多维度组合,数据量仍然非常庞大。随着后续维度的扩展,整体数据量呈指数级增长。考虑到性能分析的周期性不会太长,我们决定只保留整体聚合数据7天,并进行分表处理。如果数据量激增,我们会采取将数据转入TiDB中,并按日期进行分区存储。为了解决数据处理中的两个核心问题,我们采用了这个完整的流程。在面对如此庞大的数据时,我们需要考虑它们存储在何处。同时,我们也需要考虑如何查找和计算需要的指标。这个流程可以帮助我们更好地处理数据,提高效率。
此外,这个流程还有一个重要的作用,那就是保证数据的准确性和完整性。在数据处理过程中,我们需要遵循一定的规则和标准,以确保数据的可靠性。这样才能让我们在分析数据时得出正确的结论,更好的进行针对性的优化。
5. 性能查询展示平台web平台部分功能页面展示如下:
历史变化曲线
性能数据查询
总结在B端项目中,页面性能统计是非常有必要的,因为可以帮助我们了解实际用户的具体页面的加载速度、用户体验,以便了解当前页面的质量,并且为优化页面性能提供方向,从而提高用户满意度。
标签:
-
2023-04-21 14:06:07
全球即时看!转转B端项目页面性能统计实践<
背景由于转转前端业务方向主要偏向于C端,比如App端内H5、小程序内H5等,并且技术栈以Hybrid为主(承载容器为转转标准化webview)
-
2023-04-21 13:16:31
全球即时看!白鹤菱溪小学党员教师进社区,打造天元“美好教育”<
新湖南客户端4月18日讯(通讯员齐小慧)为传播“美好教育”理念,拓展“美好教育”内涵,促进“美好教育”绽放,4月16日
-
2023-04-21 12:41:42
大家都是怎么理财的?一万块理财有什么建议? 每日头条<
大家都是怎么理财的?可以选择每个月都定期存入一笔钱到银行,然后连续坚持12个月,这样当有急事需要用钱 "><linkhref= "http: www szonlin
-
2023-04-21 11:43:20
UNDP驻菲代表拉马钱德兰称中方援菲医废项目是三方合作典范 焦点精选<
原标题:UNDP驻菲代表拉马钱德兰称中方援菲医废项目是三方合作典范据中国驻菲律宾大使馆经商处20日消息,联合国开发计划署
-
2023-04-21 11:37:26
世界聚焦:“中油e链”为中小微企业纾困解难<
中国石油网消息(记者史智峰通讯员孙柏任思诣)截至4月18日,共享运营公司与昆仑银行合力打造的金融服务产品中油e链平台,累计协助中小微企业
-
2023-04-21 14:06:07
全球即时看!转转B端项目页面性能统计实践
背景由于转转前端业务方向主要偏向于C端,比如App端内H5、小程序内H5等,并且技术栈以Hybrid为主(承载容器为转转标准化webview)
-
2023-04-21 13:16:31
全球即时看!白鹤菱溪小学党员教师进社区,打造天元“美好教育”
新湖南客户端4月18日讯(通讯员齐小慧)为传播“美好教育”理念,拓展“美好教育”内涵,促进“美好教育”绽放,4月16日
-
2023-04-21 12:41:42
大家都是怎么理财的?一万块理财有什么建议? 每日头条
大家都是怎么理财的?可以选择每个月都定期存入一笔钱到银行,然后连续坚持12个月,这样当有急事需要用钱 "><linkhref= "http: www szonlin
-
2023-04-21 11:43:20
UNDP驻菲代表拉马钱德兰称中方援菲医废项目是三方合作典范 焦点精选
原标题:UNDP驻菲代表拉马钱德兰称中方援菲医废项目是三方合作典范据中国驻菲律宾大使馆经商处20日消息,联合国开发计划署
-
2023-04-21 11:37:26
世界聚焦:“中油e链”为中小微企业纾困解难
中国石油网消息(记者史智峰通讯员孙柏任思诣)截至4月18日,共享运营公司与昆仑银行合力打造的金融服务产品中油e链平台,累计协助中小微企业
-
2023-04-21 11:38:04
视讯!微盟启动2023 Weimob Day城市峰会 聚焦数字经济时代企业高质量发展
图说:微盟集团智慧商业事业群总裁方桐舒采访对象供图新民晚报讯(记者金志刚)4月19日,2023WeimobDay
-
2023-04-21 11:23:21
我的世界手机版防爆指令_大神来教你|全球今日讯
欢迎观看本篇文章,小勉来为大家解答以上问题。我的世界手机版防爆指令,大神来教你很多人还不知道,现在让我们一起来看看吧!1
-
2023-04-21 10:59:53
Spears:国王将小萨博尼斯列为明日G3的首发 环球热议
明天,季后赛首轮G3国王对阵勇士的比赛将移师勇士主场。名记MarcJ Spears发推透露,国王已经将小萨博尼斯列为明日比赛的首发。在G2的比赛中,
-
2023-04-21 10:15:04
追光|大运会倒计时100天 成都准备好了! 今日讯
青春作伴,共赴大运之约第31届世界大学生夏季运动会将于7月28日-8月8日在中国成都举行今天成都大运会迎来100天倒计时
-
2023-04-21 10:30:30
没有确权的农村土地、房屋被征收,能拿到补偿吗?-焦点日报
没有确权的农村土地、房屋被征收,能拿到补偿吗?,拆迁,建房,补偿,农村土地,农村宅基地
-
2023-04-21 10:18:27
[快讯]镇海股份公布年报 热点在线
CFi CN讯:镇海股份(股票代码:603637)公布镇海石化工程股份有限公司2022年年度报告摘要。2022年2021年本年比上年增减(%)2020年总资产
-
2023-04-21 09:32:44
观天下!中甲又迎来一名巴西锋霸!中超37场轰23球,曾是亚冠金靴
根据德国转会市场中国区管理员的确认,上赛季效力于中超长春亚泰队的巴西射手儒尼奥尔-内格朗自由转会四川九牛。新赛季,儒尼奥尔-内格朗这位
-
2023-04-21 08:56:21
郑州市经八路司法所:提高防骗意识 远离诈骗陷阱 当前速讯
4月18日,郑州市金水区经八路司法所联合金水区反虚假信息诈骗中心的李煜伟警官,给社区居民开展一堂生动的预防电信诈骗知识讲座,进一步加大预
-
2023-04-21 08:41:03
雪榕生物:拟投建雪榕现代食品产业项目,总投资为12亿元
2023年4月19日,雪榕生物(300511 SZ)关于签署《投资协议书》并在淮安市设立子公司的公告,公司拟在金湖县投资
-
2023-04-21 08:47:59
长腿女神素颜见“前男友”,前亚视小生从昔日美男大变样
长腿女神素颜见“前男友”,前亚视小生从昔日美男大变样,女神,素颜,万绮雯,张文慈,王祖蓝,tvb,甄子丹,前亚视小生
-
2023-04-21 08:03:21
黄体酮怎么用_黄体酮胶囊怎么服用
1、黄体酮胶囊需要根据患者的具体情况,采取特定的方式服用。2、如果患者患有月经失调和月经周期调整,可以选择一次服用两粒胶
-
2023-04-21 07:43:37
高考最高分作文酒_高考最高分
1、历史高考裸分最高分748分张镇风,总分748分。2、1998年全国高考状元。3、语文:150分;英语:150分;数
-
2023-04-21 07:01:30
冠瑞高电位治疗仪价格骗局_冠瑞高电位治疗仪价格_环球热头条
1、12800~19800随着高电位治疗仪的普及率越来越高,疗效越来越被认可。2、高电位治疗仪的价格便是一个绕不开的话题
-
2023-04-21 06:51:06
重庆:精检细修 备战五一
2023年4月19日,中国铁路成都局集团有限公司重庆车辆段重庆北动集运用车间严格落实各项检修运用要求,并对CR200J动
-
2023-04-21 06:23:40
山西松韵文化传媒有限公司-天天实时
1、山西松韵文化传媒有限公司于2018年10月16日成立。2、法定代表人王琦,公司经营范围包括:广告业务;电脑图文设
-
2023-04-21 06:31:03
热点评!一路坦途 标准引领
近期,由人民交通出版社股份有限公司出版发行的《广东省公路工程造价标准化管理指南》(简称《指南》)正式发布施行。围绕加快建设交通强国的
-
2023-04-21 05:23:53
好消息!购房补贴,在家就能申请了!
为保障群众住房需求,支持群众购房置业,激发房地产市场活力,促进房地产市场平稳健康发展,我市于2022年6月推出购房补贴政策,自政策施行以来
-
2023-04-21 05:37:04
清风头条丨古丈:让古村焕发新“廉”味
近期,湘西州古丈县坪坝镇曹家村以传承弘扬古村传统文化为依托,以清廉乡村建设为抓手,以人居环境整治为契机,村里发生了翻天覆
-
2023-04-21 04:49:16
4月20日大公司动向追踪:华为宣布实现自主可控的MetaERP研发和替换,花旗下调特斯拉目标价至175美元 当前热门
华为宣布已实现自主可控的MetaERP研发,基于华为欧拉操作系统、GaussDB等根技术,并已完成对旧ERP系统的替换。ERP是最关键、最重要的企业级IT
-
2023-04-21 04:53:28
世界播报:国家统计局:高等教育在校女生占比超半数
今天,国家统计局发布2021年《中国妇女发展纲要(2021—2030年)》统计监测报告。报告显示,在教育方面,2021年
-
2023-04-21 04:31:57
米市巷街道专项整治非机动车停放乱象,营造文明有序道路环境_环球速递
拱墅区米市巷街道辖区楼宇林立、商铺众多,非机动车违规停放现象时有发生,不仅影响市容市貌,也为车辆和行人通行带来了安全隐患
-
2023-04-21 04:23:31
全球看热讯:“第三届郎酒庄园三品节”开幕 汪俊林首提“五个意识”建设“新郎酒”
日前,“第三届郎酒庄园三品节”顺利召开。据悉,郎酒集团今年依然设置了品质、品牌、品味三类大奖,奖项增至729个。“郎酒第三届三品节”...
-
2023-04-21 04:18:49
平安资管首度提出汇丰可从分拆方案调整为战略重组方案-全球最资讯
新京报贝壳财经讯(记者潘亦纯)汇丰将于5月5日在英国召开年度股东大会,届时,会上将投票表决由小股东提出的两项议案,包括要求推动结构性改
-
2023-04-21 03:03:31
漂泊七十载魂归故土,川籍去台老兵“回家”了_天天快资讯
一张遗像,三杯清酒,七枚硬币……仪式结束,4月18日上午11点,唐毅奇骨灰正式落葬资阳市安岳县岳阳公墓。 "><linkhref= "http: www ...
-
2023-04-21 02:54:34
菏泽市文化和旅游局开展预防未成年人网络沉迷宣讲活动 焦点速递
中国山东网-感知山东4月19日讯(记者谢雯嘉)为预防未成年人沉迷网络,增强青少年网络安全意识和自我保护能力,4月18日