写点什么

百度 SSP 单页式应用性能优化实践

  • 2017-03-30
  • 本文字数:3649 字

    阅读完需:约 12 分钟

针对首页和部分页面打开速度慢的问题,百度 SSP 前端团队对单页式应用性能进行了优化。本文将介绍其中一个性能优化方案: 基于 HTTP Chunk 的首屏数据渐进式预加载方案,该方案减少了 1.2s 的加载时间。同时对比其与同构渲染方案的异同。

背景介绍

单页式应用是近几年来前端技术栈发展与落地的最典型场景,Angular、Vue、React 等,这些相关的技术栈目的都是从架构层面为单页式应用提供研发解决方案,着重解决单页式应用的研发效率。基础框架的进化也催生着关联工具链路的发展,如 Yeoman,Grunt -> Gulp -> 各种 cli,Webpack1/2, Babel 等。

随着研发链路体系的稳定成熟,在功能上能够及时满足用户后,百度展示广告部流量端前端团队(百度 SSP 前端团队)开始将精力集中关注产品的可用性层面。经过和产品,运营,用研等多个团队配合,我们走访了多位使用我们产品的用户,产出了一份流量端产品可用性得报告。除了部分交互和产品流程设计上的问题,另一个主要问题就是用户反馈整体的系统流畅性不错,但首页和部分页面打开极其慢,针对这块问题,我们开始了对单页式应用性能优化的探索和实践

本文接下来将一步一步阐述对应用首屏呈现中各个节点的拆解,并根据拆解的节点推导出我的优化思路,最终为大家介绍我提出并尝试的第一个性能优化方案: 首屏数据渐进式预加载

首屏呈现节点分析

在进行任何的性能优化之前,我们都应该先找出系统的性能瓶颈点,从而找出最有价值的优化方向。

绝大多数的单页式应用都符合 Application Shell 架构,根据这个架构我们可以看出一个应用首屏呈现节点可以分解为:请求入口页 -> 渲染应用外壳 -> 渲染首屏片段。我在此基础上进一步将三个节点细分如下:

(点击放大图像)

即对渲染应用外壳和渲染片段这块细分为:应用资源加载,应用初始化,片段资源加载,片段初始化,片段数据加载,片段渲染这些节点。

有了这些细分节点,再将埋点记录的真实用户数据代入:

(点击放大图像)

得出我们的首屏时间为:

** T(s) = T1 + … + T7 = 2800ms **

注:我们一般都将首屏资源一起与应用资源打包在一起,因此这里耗时认为是 0。

整个 timeline 如下:

(点击放大图像)

首屏数据渐进式预加载方案

根据上面的节点数据,首屏数据渐进式预加载的优化思路也得到了体现:

  1. 优化首屏数据加载节点的速度。
  2. 预先加载首屏数据,使得多个串行节点并行化。

接下来详细介绍我们的优化步骤。第 1 点会在第一步优化中体现,但核心思路和主要优化收益更多体现在第 2 点:多个串行节点并行化。

Step1:资源文件下载与首屏数据请求节点并行

为了达到资源下载与数据请求并行的效果,我们充分利用了HTTP Chunk 传输与浏览器的渐进式渲染特性

  1. 将入口页分为静态片段和数据片段:静态片段包含了各个资源标签(script,link),静态的导航栏,加载指示器等;数据片段则是包含首屏数据的内联脚本,大至如下:
复制代码
<script>window.__APP_DATA__ = { /* 相关的首屏数据 */ };</script>
  1. 浏览器请求入口页时,入口页服务器 (这里我们用了 NodeJS ) 并行 做以下操作:
  • HTTP Chunk 方式输出静态片段
  • 请求首屏数据并在所有数据请求完成后将数据片段和应用初始化代码返回给浏览器。

注:http chunk 方式输出在 NodeJS 中及其容易满足,简单的 res.write(chunk) 即可。

整体架构如下:

(点击放大图像)

浏览器的渐进式渲染特性在收到静态片段并解析后立刻去下载资源,由此巧妙的将应用资源加载节点和首屏数据请求节点并行化;当应用初始化完毕后,首屏组件直接读取 window.__APP_DATA__拿到数据渲染即可。

整个首屏呈现 timeline 变化如下:

(点击放大图像)

最终并行化这块耗时为:Max(下载资源文件,请求首屏数据输出片段) = 1000ms。

根据变化后的节点我们算出首屏呈现时间为: 2350ms

首屏呈现耗时的通用计算公式变为:

下载静态片段 + Max(下载资源文件,请求首屏数据) + 应用初始化 + 首屏初始化 + 首屏渲染

Step2:应用初始化,资源文件下载,首屏数据请求节点并行

在 Step1 的基础上继续分析,应用初始化节点耗时也很明显,同时该节点要进行必须等待资源文件下载完毕,但理论上可以不依赖我们的首屏数据,还是可以让其和首屏数据请求并行。

这里我们无法在 Step1 方案上直接将应用初始化和数据请求并行化,主要原因在于当首屏数据请求时间大于资源加载 + 应用初始化完成时间时,应用会在没有数据的情况下进入收入首屏渲染节点,从而导致异常。

解决方案是将数据片段的输出变成 promise 片段:

  1. pending promise 片段,与静态片段一起输出,大概如下:
复制代码
<script>
window.__APP_DATA__ = {
RESOLVERS: {}
userInfo: new Promise((resolve, reject) => {
// 超时认为失败
let timer = setTimeout(reject.bind(null, {message: 'timeout'}), 12000);
window.__APP_DATA__.userInfo = (err, data) => {
clearTimeout(timer);
err ? reject(err) : resolve(data)
}
})
};
</script>
  1. resolve promise 片段,该片段在数据请求成功返回后输出,大概如下:
复制代码
<script>window.__APP_DATA__.RESOLVERS.userInfo(null, data); </script>
  1. reject promise 片段,该片段在数据请求失败后输出,大概如下:
复制代码
<script>window.__APP_DATA__.RESOLVERS.userInfo(error); </script>

即此时应用初始化完毕后可以无视首屏数据的完成度,直接进入首屏渲染节点,组件在数据 promise 被 resolve 后渲染即可:

复制代码
window.__APP_DATA__.userInfo.then(data => component.render());

通过对数据片段的 promise 化改造,使得应用初始化节点也加入了并行队列。

整个首屏呈现 timeline 变化如下:

(点击放大图像)

根据变化后的节点我们得到首屏呈现时间为: 1800ms

首屏呈现耗时的通用计算公式变为:

下载静态片段 + Max(下载资源文件 + 应用初始化,请求首屏数据) + 首屏初始化 + 首屏渲染

优化小结

经过上述 2 个步骤改进,我们应用首屏呈现时间从 2800ms -> 2350ms -> 1800ms,总体效果约为 36%,可以看到是收益还是很可观的。

在实际项目中耗时是在1600ms左右,比 1800ms 还要小,主要原因如下:

  1. 用户在请求入口页中半个 RTT 时间,服务器就开始了数据请求。
  2. 数据请求在服务端进行减少了浏览器与服务端的请求创建开销,同时数据请求在内网进行,总体调用速度也会加快。

当首屏数据请求数超过浏览器并发请求数时,该方案收益会更明显,因为 NodeJS 端没有并发限制,甚至在 NodeJS 端与后端服务的交互中可以采用更高效的协议如 HTTP2 来提高调用速度。

与 SSR 方案的对比

看到这里,相信很多人会问,为啥不用服务端渲染直出 HTML 呢,或者和服务端渲染方案相比有何优势?

事实上,一开始我和大多数人想到的优化方案就是服务端渲染,但真正的障碍在于服务端渲染依赖视图层框架的支持,而我们的项目历史悠久,视图层框架并不支持这一点,为了优化而丧失产品的稳定性得不偿失。

当然,在另辟蹊径使用了数据渐进式预加载方案后,我总结该方案与 SSR 的对比如下。

优势

  1. 对客户端代码来说数据渐进式预加载方案实现成本非常简单,基本可以做到透明化,我们在实际的开发过程中采用基于 uIoC( https://github.com/ecomfe/uioc ) 提供的 AOP 拦截方案,通过配置化的方式让客户端的代码改造仅局限在配置文件,应用代码基本未改动。
  2. 对 NodeJS 端来说,分层合理的应用只需要将数据层简单适配下 NodeJS 端即可完成数据渐进式预加载,这对底层基础框架在视图层没有支持同构的应用来说,整个改造成本可以说大大减小,且收益明显。我们目前的应用基于自有的一套 MVC 框架,仅仅是将 Model 层简单适配 NodeJS 端执行输出数据。
  3. 服务端渲染方案如果未能提供较基于 BigPipe 的渲染,总体的页面呈现速度还是不如数据渐进式预加载的,且目前我也暂时还没有在三大框架中发现有一套基于 BigPipe 的服务端渲染方案。

不足

整体呈现速度可能不如结合了 BigPipe 的服务端渲染方案,但这点没有经过论证,毕竟数据渐进式预加载与服务端同构渲染的区别仅仅在于渲染环节放在客户端还是服务端:渲染看的是 CPU,服务端的 CPU 资源是有限的,要服务诸多请求,而客户端渲染则基本无此压力,渲染能力未必弱于服务端。

总结

我们在单页应用的性能优化上基于很朴素的并行化理念实施了首屏数据渐进式预加载方案,在实际项目中也得到了较为明显的效果,减少了 1.2s 的加载时间,整体的节点变化如下:

优化前:

(点击放大图像)

优化后:

(点击放大图像)

最终数据渐进式预加载方案的首屏呈现时间计算公式为:

下载静态片段 + Max(应用资源加载 + 应用初始化,请求首屏数据) + 首屏初始化 + 首屏渲染

这里忽略了影响很小的片段传输时间,有打算尝试的朋友可以将自己应用的相关节点数据代入计算即可。

数据渐进式预加载,服务端同构渲染,客户端渲染三种方案各有优缺和场景,个人未来计划是将三种方案结合实时流量数据动态切换:在服务器压力不大时用同构渲染;服务器压力较大时用数据预加载;服务器压力很大时用客户端渲染。


感谢韩婷对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2017-03-30 17:342637

评论

发布
暂无评论
发现更多内容

琼海市等保测评机构有哪些?在哪里?

行云管家

等保 等保测评 琼海

TiDB SEMI JION(半连接)优化实践

TiDB 社区干货传送门

实践案例

TDengine 与北微传感达成合作,解决传统数据库性能瓶颈

TDengine

数据库 tdengine 时序数据库

Acrobat Pro DC 2019 for mac中文破解版 附激活补丁 及安装包

理理

ODC24 AI服务生态分论坛:全新智慧服务引擎 带动服务分发增长

科技热闻

TiDB 关联子查询优化实践

TiDB 社区干货传送门

实践案例

如何使用OpenAI文本Embedding模型构建AI系统

Zilliz

非结构化数据 Milvus zilliz cloud Embedding模型

QCN9024 vs. QCN9224: Wi-Fi 6 and Wi-Fi 7 Chipset Showdown - Performance, Configuration, and Applications Compared

wifi6-yiyi

5G wifi 6G

分布式数据库安全可靠测评名录之平凯数据库(TiDB企业版)

TiDB 社区干货传送门

数据库架构设计

ABAQUS软件包括哪些模块?ABAQUS介绍

思茂信息

功能 abaqus 有限元

地平线与英伟达工具链 PTQ 工具功能参数对比与实操

地平线开发者

自动驾驶 算法

audirvana mac破解版 苹果电脑原生无损音乐播放器

理理

OPPO应用分发的燎原之火,照亮开发者的增长之路

脑极体

AI 应用

利用代理ip爬取Alibaba海外版数据

Yan-英杰

Python 后端

TiDB 社区第四届 1024 程序员心愿节来啦!没有 Bug ,只有 Luck!参与留言活动,许愿你想要的 TiDB 产品功能、周边、TiDB 课程 & 考证优惠,一起庆祝 1024 吧!

TiDB 社区干货传送门

解锁电商决策新视角:淘宝天猫商品评论数据API接口的深度探索

代码忍者

pinduoduo API API 性能测试

abbyy finereader ocr mac破解版 ocr文字识别软件 v15.2.13中文版

理理

轻帆云移动端智能语音提单:一键触达,智能工单新体验,助力高效运营

云智慧AIOps社区

ITSM软件 IT服务管理 工单管理 智能工单

【一文读懂LangGraph、AutoGen、Swarm】大模型时代AI智能体框架核心技术原理

小奇同学

智能体 多智能体 LangGraph AutoGen Swarm

深圳计算科学研究院YashanDB数据库系统斩获“鼎新杯”二等奖

Geek_2d6073

高效项目管理:利用低代码平台简化项目实施流程的策略与实践

天津汇柏科技有限公司

项目管理 低代码平台

foobar2000 mac下载 多功能音频播放器 v2.6.5免激活版

理理

云管理平台公司大家都选哪家?选择时候需要考虑哪些因素?

行云管家

云计算 云管理

格睿科技完成数百万美元融资,助力可观测和物联网领域高效数据价值挖掘

Greptime 格睿科技

数据库 融资

一文探索RareShop:首个面向消费者的RWA NFT商品发售平台

NFT Research

NFT\ nft工具

PingCAP 与百胜中国联合实验室发布阶段性成果,以技术创新引领餐饮行业数字化转型

先锋IT

TiDB 新版本:更稳、更快、更好的数据库体验

TiDB 社区干货传送门

版本升级 新版本/特性解读

Milvus 到 TiDB 向量迁移实践

TiDB 社区干货传送门

迁移 实践案例 TiDB Cloud TiDB Vector

构建行业应用生态:云原生应用市场简化企业软件安装

北京好雨科技有限公司

云原生 企业级应用 行业生态 应用市场

TDengine 3.3.3.0 版本上线:优化监控、增强 MongoDB 支持

TDengine

数据库 tdengine

TiDB 关联子查询及半连接的优化实践

TiDB 社区干货传送门

性能调优 实践案例

百度SSP单页式应用性能优化实践_最佳实践_邓欣欣_InfoQ精选文章