前端性能优化:当页面渲染遇上边缘计算

云栖号资讯:【点击查看更多行业资讯】
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

前端性能优化:当页面渲染遇上边缘计算

阿里妹导读:当前几种常见的前端性能优化方案仍然不可避免地会存B V ` + ~ S在一些缺点。本文在 ESI (Edge Side Include) 的O / m基础上,提出了一种新的优化思路:边缘流式渲染方案(ESR),即借助 CDN 的边缘计算能力,将静态内容与动态内容以流式的方式,先后返回给用户。

背景

对于 web 页面来说,首跳场景(例如 SEO、付费引r R ~ , t流)的性能普遍比二跳场景下要差。原因有多种,主要是8 ^ b @ u ; 2 1首跳用户在连接复用,和本地资源缓存利A N t ] 8用方面,有很大的劣势。首跳场景下,很多在 : j d @端上的优化手段(预加载,预执行,预渲染等)无W a b c W法实施。

在客户端j @ P T : ( !缓存能力无法利用的情况下,利用 cdni 8 c离用户近的特性,可以结合缓存做一些性能优化。

思路5 c C U

思路 1:SSR

为了性能优化考虑,我们一般都i = T ~ x x B *会通过服务端渲染(SSR) ,将首屏动态内容直l A z z /接服务端输出。

前端性能优化:当页面渲染遇上边缘计算

这种方式的优点是一次 html 返回即可包含页面主体内容,不需要浏览器二次请求接口后再用 js 渲染。但这种方式的缺点也比较明显,对于距离服务端远,或者服务端处理时间较长的场景,用户会看到较长时间的白屏。而且即使 html 返回完成了,用户并不会S = j t ! `立即看到内容,页面还需要加载前置的 js,css 等资源后,? N w % G I 9才能看到内容。

思路 2:CSR + CDN

为了减少白屏时间,考虑利用 CDN 的边缘缓存能力,可以把页面 html 直接缓存在 cdn 节点上) & k i V ? _。但对于大部分场景来说,页面的主体内容都是动% A Z 0 9 F h |态,或者个性化的,把全部 html 内容缓存在 cdn 上对于业务影响较大,很有少场景能接受。那么换个思路,只把 html 静态U w ? s D部分缓存在 cdn 上呢?其实这个思路也是一个很常见的操作,即把 html 的静态框架部分缓存在 cdn 上,让用户能快速看到部分内容,然后再在客户端发起异步请求,获取动态内容并且渲染(CSR)。CSR + CDN 模式下的渲染时序图如下:

前端性能优化:当页面渲染遇上边缘计算

这种方式的优点是页面静态框架缓存在 cdn 上,G ~ 7 ) ; ? M用户可以快速看到页面框架内容,减少白屏等待焦虑。缺点是完整的页面内容需要$ B :再执行 js ,拉取异步接口回来后再进行渲染。最终有意义的动态内容展示出来的时间,比 SSR 更晚。

思路 3:ESI

CSR + CDN 的方式,很好地解决了白屏时间问题,但带来了动态内容展示的延时。之所以有这个问题,是因为我们把页面的动态F b X G v G f (内容和静态内容分割B 0 x ? o到了两个阶段中,并且是串行的,而且串行过程中还穿插了 js 的下载和执行。有什么办法把动态内容和静态内容在 CDN$ b I 上整合起来呢?

ESI (Edge Side Include) 给了我们一个很好的思路启发,ESI 最初也是 CDN 服务商们提出的规范,可. m % $ q . 9通过 html 标签里加特定的动态标签,可V ? x } ~ w 2 H o让页面的静态内容缓存在 cdn 上,动态内容可以自由组装。ESI 的渲染时序图如下:

前端性能优化:当页面渲染遇上边缘计算

这个方案看起来很美好,可以W 1 m y 把静态的部分缓存在 CDN 上了,动态部分在用户请求时会动态请求和拼接。但最关键的问题在于,ESI 模式下,最终返回给用户的首字节,还是要等到所有动态内容在 CDN 上都获取和拼接完成。也就是并没有减少白屏时间,只是减少了 CDN 和服务器之间内容传输的体积,带来的性能优化收益很小。最终效果上与 SSR 区别不大。

虽然 ESI 的效果不符合我们预期,但给了我们很好的思考方向。如果能把 ESI 改造成可先返回静态内容,动态内容在 CDNf c R C q [ r = 2 节点获取到之后,再返回给页面,就可以保证白屏时间短并且动态内容返回不推迟。如果要实现N 4 e y m类似于B j /流式 ESI 的效果,要求在 CDN 上能对请求进行细粒度的操作,以及流式的返回。CDN 节点上支持这么复杂的操作吗?答案是肯定的:边缘计算。我们可以在 CDN 上做类似于浏览器的 service worker 的操作,可对请求和响应做灵活的编程。

基于边缘计算的能力,我们有了一种新的选择:边缘流式渲染方案(ESR)。方案详情如下。

渲染流程

方案的核心思想是m k @ Y I,借助边缘计算的能力,将静态内容与动态内容以流式的方式,先后返回给用户。cdn 节点相比于 server,距离用户更近,有着更短的网络延时。在 cdn 节点上,将可缓存的页面静态部分,先快速返回给用户,同时在 cdn 节点上发起动态C ) w a O q *部分内容请求,并将动态内容在Q P q N ( I , { 4静态部分的响应流后,继续返回给用户。最终页面渲染的时序图如下:

前端性能优化:当页面渲染遇上边缘计算

从上图可以看出,cdn 边缘节点可以很快地返回首字节和页面静态部分内容,然后动态内容由 cdn 发起, w + f % }向 server 起并流式返回给用户9 r ` z @ -。方案有以下特点:

  • 首屏 ttfb 会很短,静态内容(例如页面 Header 、基本结i f T构、骨骼图)可以很快s ? t l u o看到。
  • 2 Y r 6 P F L C 0态内容是由 cdn 发起,相比于传统浏览器渲染,发起时间更早,且不依赖浏览器上下载和执行 js。理论上,最终 reponse 完~ r P 0 # G v ?结时间,与直接访问服务器获取完整动态页面时间一致。
  • 在静态内容返回后,已经可以o J 1开始部分 html 的解析,以及 js, css 的下载和执行。把一些阻塞页面的操作提前进行,等完整动态内容流式返回后,可以更快地展示动态内容。
  • 边缘节点与服务端之- U ? U % v )间的网络,相比于客户端与服务端之间的网络,更有优化空间。例如通过动态加速,以及 edge 与 server 之间的连接复用,能为动态请求减少 tcp 建连和网络传输开销。以做到最终动态内容的返回时间,比 client 直接访问 server 更快。

demo 对比

目前在 alicdn 上对主搜页面做了一个 demo (https://eJ 9 I k qdge-routine.m.alibaba.com/), 下面是Y ( 4 u l | ( B W在不同网络(通过 charlp c 6 l ) Qes 的 n[ V U ~ c W YetworkX % h B 3 M throttle 配置限速)情况下,与原始页面的加载对比:

不限速(wifi)

前端性能优化:当页面渲染遇上边缘计算

限速 4G

前端性能优化:当页面渲染遇上边缘计算

r Y ; 1 & C 9 _速 3g

前端性能优化:当页面渲染遇上边缘计算

从上面结果可以看出,在网速越慢的情况下,通过 cdn 流式渲染的最U S ] E f ~终主要元素出来的时间比原始 ssr 的方式c e @ d s出来得越早。这与实际推论也符合,因为网络越慢,静态资源加载时间越慢,对应的浏览器提前加载静态资源带来的效果也越明显。另外,不管在什么网络情况下,cdn 流式渲染方式的白屏时间要短很多。

整体架构

架构图

前端性能优化:当页面渲染遇上边缘计算

边缘流式渲染

1 模板

模板就是一个类似于包含 ESI 区块的语法,基于模板,会将需要动态请求的内容提取出来,把可以静态返回的内容分离出来并缓存_ e Y起来。所以模板本质上定义了页面动态内容和静态内容。

在流式渲染过程中,会从上到下解析页面模板,如果是静态内容,直0 z j ( b M 5接返回给用户,如果遇到动态内容,会执行动态内容的 fetch 逻辑。整个Y } $ o S 1 $ 3 Q过程中可能有静态和动态内容交替出现。

设计有以下几种类型的模板。

1)原始 HTML

这种模板对现有业务的侵入性最小,只需要在现有的 SSR 页面内容里加上一定的标签,即可把页面中动态部分申U , % 3明出来:

<ht| { y [ y  4 Rml>
<head& 4 %gt;
<linkrel="stylesheet"type="text/css"href="https://yq.aliyun.com/articles/762597/index.css">
<scriptsrc="https://yq.aliyun.com/artie Y H +cles/F i 8762597/index.js"><# N ] m _/script><metaname="esr-version"P C n *content="0.0.1"/>
</head&C * cgt;
<body>
<d, J A Eiv>staic content....</div>
<scripttype="esr/snippet/start"esr-i? A Md="111"co} m / Q  n 5ntent="SLICE"></script>
<div>dynamic content1....</div>
<G ( e ` Y U R , 2scripttype="esr/snippet/end"></script&gN O |t;
<div>staic content....</div>
<scripttype="esr/snippet/start"esr-o } k p _ W did="222"content="httpi W ~s://test.alibaba.comL _ % I : h/snippet/222"&g` * 8t;</script>
<divid="222">
dynamic content2....
</div>
<scripttype="esr/snippet/end"></script>
</body>
</html>

2)静态模板(暂时没有关联的实际场景)

l _ W 7 7 ]种模板需要单独把模板发到 cdn 上(未t ; G Z G / 5 R E来如果渲染层接c ! [入了 FASS 网关和 SSR ,在这块可以和他们共用模板内容,并且在工作流中e u l } + Y . m发布模板时自动同步到 cdn 上一份,同时清y D g D D* f q X b i [ L g cdn 上缓存)。动态的内容有两种渲染方式。一种是利用后端 SSR 出来的动态 html 片断,另一种是后端提供动态数据,由边缘节进行动态html片断渲染。

使用 SSR 动态 html 片断的好处是,不需要在边缘上做 htmQ y { V H B 2 Pl 模板渲染,并且不需要开发者写两套模板逻辑s k j }。缺点是需要后端有 SSk z w } W o v & cR 能力,并且动态内容传输体积较大。

使用边缘节点渲染动态2 6 @ html 内容的好处是,后端只需要提供动态数据8 C /,不需要 SSR 能力(但前端要有 CSR 的能力做降级兜底l T 2 w b Q T),并且传输的动态内容体积小。切点是边缘节点上无法流式透传动态内容,需要等完整下载到边缘节点上,处理E = J ] 8 S +后再返回给用户。

<html>
<head>
<linkrel="stylesheet"type="text/E ~ 9 1 Y I X X lcss"hC V t , nref="https://yq.aliyun.com/articles/762597/M H l Vindex.css">
<scriC + 6 w w qptsrc="Y U 8 ihttps://yq.aliyun.com/arti9 W u ) o % Ucles/762597/index.js"&gs P Y 2 y `t;</script>
</head>
<body>S J 3 e 6 g 7 t
<div>staic cont: H ment....</div>
<scripttype,  u K="esr/bx K f ( klock"esr-id="111"content="https://test.alibaba.com/snippet/111"></^ ! @ p f /script>
<div>staic contentX b a [ G f....</div>
<scripttI N ype="esr/template"esr-id="222"content=1 k p 6 = m Q _"https& x [ H R 0 R 9 )://test.alibaba.com/api/data">
<div>
{$data.name}
</div>
</script>
</body>
</html>

2 静态内容展现

静态内容来自于模板。对于不同模板类型,获取静态内容的方式不一样。对于 “原始 HTML” 类型的模板,7 V + 4静态内容会从首次动态请求返回的完整 HTML 中,根据 html 注释标记提取出来,并存储到 edge 缓存上。对于 “静态模板”,会通过拉取 CDN 的的模板文件 ,并存储? ( $ 8 @ :到 edge 缓存上。静态内容有缓存过期时间和版本号。$ D G W s U

模板一开始的静态内容会在m y [ 3 , g响应时直接返回给用户。后续的静态内容(例如 html 和 body 的闭合标签)有两种方式:

一种是等待动态内容返回后,再写到响应流中。这种方式对 SEO 比较友好,但缺点是动态内容会阻塞住后续静态内容,并且如果有多个动态内容区块的话,无法实现先返回的动态模板先展示,只能依次展示。

另一种方式是先把静态内容完全返回,然后动态内容以类 bigpipe 的方式,通过脚本把内容插入到对应的坑位。这种方式的优点是静态内容可以一开始就i $ 6 9 [ e完整展示7 8 X,且多个动态内容可以先到先展示。缺点是对 SEO 不友好(因为动态内容是能进 js 插进去的)。

3 动态内容

动态内容是在渲染过程中,解析到需要动态获取的区域,会在 edge 上发起动态内容请求。动态内容支持以动态加速的q g 8 D形式到达服务端(源站)。连续节点与后端的3 f z c = 1 q动态的内容交互,分为三种方式:

第一种是后端动态内容返回的是全量的页面,需要通过注释标记来从内容中提取。这种方式的优点是对现有业务侵入较小,缺点是动态内容传输体积大,并且需要下载完整 html 后再截取动态内容。

第二种是后端动态内容只返回动态区块的内容,这种方式的优点是可以将动态响应流式返回给用户,缺点时需要页面单独对外提供一个只返回动态区块内容的I 6 + url。

第三] y P s P w f种是后端动态内容只返回数据,配合静态模板中的动态渲染模t 4 + % - !板,在边缘节点上渲染出动态 html 后返回给用户。优点是与后端传输数据量小,且不需要后端有 SSR 能力。缺点是需要开发者多维护一套模板逻辑,并且在边缘节点上做复杂的模板渲染可能会有 cpu 开销和限制。

用户和边缘节点的动态内容交互,分为两种形式:

瀑布流式(对应路由配置里的 WATER_FALL ): 动态内容以瀑布流的形式依次返回。虽然在边缘节点上多个动态内容加载的操作是并行的,但对于用户来说,会从上到下依次展示页面内容。这种方式优点是对 SEO 友好,并且不影响页面} { t P ?模块的加载顺序。缺点是多个动态模块时,无法看到整体页面的框架,首个动态块的内_ X ^ | t (容会阻塞后续动态块内容的展示,且页面底部的 js css 资源无法提前加载和执行。

嵌入式(对应路由配置里的 ASYNC_INSERT ):静态内容一次性全部返回,其中动态部分内容会先占一些坑位。后续动态内容会以 innerHTML 的形s E 4式,插入到先前占的坑中。这种方式优点是页面底部的 js css 资源无法提前加载和执行,并且页面可以先看到一个全貌。缺点是对 SEO 不e M ` / u . Q T友好f G s,且页面模块的执行顺+ h Y 5序会根据动态块返回速度有所变化,需要在浏o , 7 x 6 F ~ j览器端页面逻辑里做一些判断和兼容。

边缘路由

路由配置:
https://g.alicdn.com/edgerender/config.json

{
version: '0.0.1'//配置版本号m u i I * r
origin: 'us-proxy.alibaba.com',
host: 'edge.alibaba.com'
pages: [
{
pageName: 'seo', //页面名称标识
match: '/abc/efg/.*', //页面path匹配正则字符串
renderCo- R {nf: {
//渲染配置
renderType: 'ESR', //边缘渲染
templateTypM B ^ ~e: 'FULL_HTML', //模板类型:将SSR出的完整html作为模板
dynamicMode: 'WATER_FALL|ASYNC_INSEi H I # PRT', // 动态内容append返回方式:瀑布流返回|异步e r C L *填坑(innerHTML)
templateUrl: ''// 模板url
}
},
{
pageName: 'seo',
match: '/abc/efg/.*',
renderConf: {
renderTys 1 M h 1pe: 'ESR',
templateType: 'STATIC', // 静态模板,可通过cdn url获取
dynamicMode:- p `  T } k 'WATER_FALL|ASYNC_l i q x . QINSERT', // 动态内容append返回方式:瀑布流返回|# B ) & +异步填坑(innerHTML)
templateUrl: 'https://g.alicdn.com/@g/xxx.html'
}
},[ i T C U n
{
pageName: 'jump',
match: '/jumpI W H 2 C X 0/.*',
renderConf: {
renderType: 'REDIRECT_302', // 302跳转
rewriteUrl: 'https:/= E /jump'
}
},
{
pageName: 'proxy',
match: '/proxy/.*',
renderConf: {
renderType: 'PROXY_PAJ o + RSS', // 301跳转
rewriteUrl: 'https://proxypassurl'
}
}
]
}

路由可以认为是边缘计算的一个入口,只有在路由U t & 1 0 ( /配置中的页面,才会走对应的渲染流程。否则页面会直接走回源,获取页面完整内容T I M & Y g `。上面的 json 是目前设计的路由配置文件。配置文件最终会在一个静态资源的方式,走覆盖式发布发到 assets cdn 上。同时,为了支持配置发布灰度,线上会存在灰度版本和全量1 H - / D版本的两个配置,在路由代码里配置固定比例,加载灰度或者全量版本的配置。

目前在路由里设计了三种渲染模式,分别是流式渲染、重定向和反向代理。重定向和反向代理的配置比较简单,与 nginx 配置类似,只需要提目标 url 即可。

稳定性

影响范围控制

CDN 开关:域名按区域、按比例切流,同时可随时从 cdn 上把流量切回统一接入。

边缘计算 SCOPE 开关:cdn 上配置边缘计算覆盖路径,控制边缘计算只运行在部分M J , L t Z n路径下。

边缘计算路由开关:边缘计算中通过读取路由配置,控制只有部分页面走流式渲染,否则请求直接走动态加速获取完整页面内容。

异常处理

dns 开关,= a D b 8 N w L如出现 cdn 严重问题,直接 dns 回切到统一接入。

如果边缘计算基础功能L S X j z | c & 2出现异常,在 cdn 配置平台上关闭所有路径的边缘计J l a 0 o i算,走默认的动态加速。

如果在进了边缘渲染,在没有返回任何响应内容给客户端前,就/ k 3 Q ! e出现了错误,捕获错误并降级到获取完整页面内容。

如果进了边缘渲染,已经返回了静态部分的响应给客户端,然后在边缘节点了加载动态内容出了问题(超时、http 错误码、与静态内容版本号不匹配),返回J | 2 |一个 location.reload() 的 script 标签,并结束响应,让页面强制刷新。刷新时可带上 byh ( _ ( c ] 4 W #pass 边缘计算的 qu9 q * 3ery 参数以保证刷新时不走边缘渲染。

灰度

1)边缘计算代码灰度

本身平台支持灰度发布边缘计算代码。

2)路由配置灰度

在边缘计算代码里,根据固定比例,加载灰度版本和正式版本的两个配置 url。灰度发布4 ` 6 ^ V 0时只发布灰度配置,全量发d d 0 m M 8 u a布时发布全量配置。发布的同时清? 9 j / c P S T空 cdn 缓存。

3)页面内容灰度

给灰] S E w A度页面一个特殊的模板版本号,遇到这个版本号的话,就不走边缘渲染。

平滑发布

前后端分离的发模式下,有一个普遍存在的问题:平滑发布。当页面c O : I的静态资源(js,css )的发布,不是与后端一起发5 # O布时,可能引起后端返回的 HTML 内容与前端的 js,css 内容不匹配的问题, t } L。如果两者之间的不匹配没做兼容处理,! A { h 0 # a r可能会出现样式错乱或者 docume! = F Z .nt 选择器找不到元素的问题。

解决平滑发布的P C J x 7一种方式是,在做前后端同时变更的需求时,在代码上做兼容。这样先后发布就不影响页面可用性。

另一种方式是通过版本号。在后端页面上手动配置版本号。当有不兼容发布时,先发前端资源,然后后端手动修改版号,保证只有发布成功的| Y e后端机器, HTML 里引用的才是新版本的静态资源。

平滑发布的问题其实在分批发布和 Beta 发布的7 p / P p {场景一直存在。只是在 ESR 的场景,我们把静态部分缓存在 cdn 上,会使前后端不一致的可能性更大。为了解决这个问题,需要对应业务的开发者进行发布时的风险识别。如果已经做了兼容,可以不用做特殊处理。但如果没有兼容,需要 g K V在修改页面模板的版本号,新版本的动态内容,在遇到版本号不匹配的静态内容时,会放弃本次# 2 y j ~ l *流式渲染,保m * $ z L _ # $ p证页面不出动态内容和静态内容的兼容问题。

边缘 cdn 服务商

目前各大 cdn 服务商对边缘计算的支持情况如下:

alicdn

支持类 service worker 环境的边缘计算,功能满足需求。

海外节X s e ! O x点目前还有限,部分区域性能可与akamak g g s $ M xi 对标甚至超过,但有些域名性能因节点少的原因还是比1 } P akamai 稍差。

akamai

只支持简单的请求改写计算,不满足边缘渲染的需求。

ESI 可以组装 2 |动态和静态内容g & U A [ t Y J G,但u + Q不支持流式,动态内容会阻塞首屏。

海外节点多,在一些地区下相比于 alicdn 有性能优势。

cloudfare

支持类 service worker 环境的边缘计算,功能满足需求。

没有使用经验,如果要用的话可能流程比较复杂。

落地计划

我们会在一个典型的首跳场景进行实验。目前已经在灰度上线,通过 weby g A d Cpagetest 在印尼测试进方案和不进R v y P ! = @方案的对比,可以/ p 3 |看出优化效果:

ttfb 减少 1s

白屏时间减少 1s

核心内容展示时间减少 500ms

webpagetest 对比结果:
https://www.webpagetest.org/video/view.phJ : X a Rp?id=1915 | G # o Y202_24218f13985a6403b911* } i & } L q J Ybeb7f94d6d1a1940fc13

【云栖号在线课堂】每天都有产品技术专家分享!
Q A G n m = 8 K程地址:https://yqh.aliyun.com/live

立即加入社w ! f S Y群,与专N 8 r u ~ /家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-05-27
本文作者:灵邀
本文来自:“阿里技术公众号”,v } ( C g p | x _了解相关信息可以关注“阿里技术”