阿里妹导读:当前几种常见的前端性能优化方案仍然不可避免地会存在一些缺点。本文在 ESI (Edge Side Include) 的基础上,提出了一种新的优化思路:边缘流式渲染方案(ESR),即借助 CDN 的边缘计算能力,将静态内容与动态内容以流式的方式,先后返回给用户。
文末福利:下载《覆盖全端业务的大前端技术》电子书。
-
首屏 ttfb 会很短,静态内容(例如页面 Header 、基本结构、骨骼图)可以很快看到。
-
动态内容是由 cdn 发起,相比于传统浏览器渲染,发起时间更早,且不依赖浏览器上下载和执行 js。理论上,最终 reponse 完结时间,与直接访问服务器获取完整动态页面时间一致。
-
在静态内容返回后,已经可以开始部分 html 的解析,以及 js, css 的下载和执行。把一些阻塞页面的操作提前进行,等完整动态内容流式返回后,可以更快地展示动态内容。
-
边缘节点与服务端之间的网络,相比于客户端与服务端之间的网络,更有优化空间。例如通过动态加速,以及 edge 与 server 之间的连接复用,能为动态请求减少 tcp 建连和网络传输开销。以做到最终动态内容的返回时间,比 client 直接访问 server 更快。
<html>
<head>
<linkrel="stylesheet"type="text/css"href="index.css">
<scriptsrc="index.js"></script><metaname="esr-version"content="0.0.1"/>
</head>
<body>
<div>staic content....</div>
<scripttype="esr/snippet/start"esr-id="111"content="SLICE"></script>
<div>dynamic content1....</div>
<scripttype="esr/snippet/end"></script>
<div>staic content....</div>
<scripttype="esr/snippet/start"esr-id="222"content="https://test.alibaba.com/snippet/222"></script>
<divid="222">
dynamic content2....
</div>
<scripttype="esr/snippet/end"></script>
</body>
</html>
<html>
<head>
<linkrel="stylesheet"type="text/css"href="index.css">
<scriptsrc="index.js"></script>
</head>
<body>
<div>staic content....</div>
<scripttype="esr/block"esr-id="111"content="https://test.alibaba.com/snippet/111"></script>
<div>staic content....</div>
<scripttype="esr/template"esr-id="222"content="https://test.alibaba.com/api/data">
<div>
{$data.name}
</div>
</script>
</body>
</html>
-
一种是等待动态内容返回后,再写到响应流中。这种方式对 SEO 比较友好,但缺点是动态内容会阻塞住后续静态内容,并且如果有多个动态内容区块的话,无法实现先返回的动态模板先展示,只能依次展示。
-
另一种方式是先把静态内容完全返回,然后动态内容以类 bigpipe 的方式,通过脚本把内容插入到对应的坑位。这种方式的优点是静态内容可以一开始就完整展示,且多个动态内容可以先到先展示。缺点是对 SEO 不友好(因为动态内容是能进 js 插进去的)。
-
第一种是后端动态内容返回的是全量的页面,需要通过注释标记来从内容中提取。这种方式的优点是对现有业务侵入较小,缺点是动态内容传输体积大,并且需要下载完整 html 后再截取动态内容。
-
第二种是后端动态内容只返回动态区块的内容,这种方式的优点是可以将动态响应流式返回给用户,缺点时需要页面单独对外提供一个只返回动态区块内容的 url。
-
第三种是后端动态内容只返回数据,配合静态模板中的动态渲染模板,在边缘节点上渲染出动态 html 后返回给用户。优点是与后端传输数据量小,且不需要后端有 SSR 能力。缺点是需要开发者多维护一套模板逻辑,并且在边缘节点上做复杂的模板渲染可能会有 cpu 开销和限制。
-
瀑布流式(对应路由配置里的 WATER_FALL ): 动态内容以瀑布流的形式依次返回。虽然在边缘节点上多个动态内容加载的操作是并行的,但对于用户来说,会从上到下依次展示页面内容。这种方式优点是对 SEO 友好,并且不影响页面模块的加载顺序。缺点是多个动态模块时,无法看到整体页面的框架,首个动态块的内容会阻塞后续动态块内容的展示,且页面底部的 js css 资源无法提前加载和执行。
-
嵌入式(对应路由配置里的 ASYNC_INSERT ):静态内容一次性全部返回,其中动态部分内容会先占一些坑位。后续动态内容会以 innerHTML 的形式,插入到先前占的坑中。这种方式优点是页面底部的 js css 资源无法提前加载和执行,并且页面可以先看到一个全貌。缺点是对 SEO 不友好,且页面模块的执行顺序会根据动态块返回速度有所变化,需要在浏览器端页面逻辑里做一些判断和兼容。
{
version: '0.0.1'//配置版本号
origin: 'us-proxy.alibaba.com',
host: 'edge.alibaba.com'
pages: [
{
pageName: 'seo', //页面名称标识
match: '/abc/efg/.*', //页面path匹配正则字符串
renderConf: {
//渲染配置
renderType: 'ESR', //边缘渲染
templateType: 'FULL_HTML', //模板类型:将SSR出的完整html作为模板
dynamicMode: 'WATER_FALL|ASYNC_INSERT', // 动态内容append返回方式:瀑布流返回|异步填坑(innerHTML)
templateUrl: ''// 模板url
}
},
{
pageName: 'seo',
match: '/abc/efg/.*',
renderConf: {
renderType: 'ESR',
templateType: 'STATIC', // 静态模板,可通过cdn url获取
dynamicMode: 'WATER_FALL|ASYNC_INSERT', // 动态内容append返回方式:瀑布流返回|异步填坑(innerHTML)
templateUrl: 'https://g.alicdn.com/@g/xxx.html'
}
},
{
pageName: 'jump',
match: '/jump/.*',
renderConf: {
renderType: 'REDIRECT_302', // 302跳转
rewriteUrl: 'https://jump'
}
},
{
pageName: 'proxy',
match: '/proxy/.*',
renderConf: {
renderType: 'PROXY_PASS', // 301跳转
rewriteUrl: 'https://proxypassurl'
}
}
]
}
-
CDN 开关:域名按区域、按比例切流,同时可随时从 cdn 上把流量切回统一接入。
-
边缘计算 SCOPE 开关:cdn 上配置边缘计算覆盖路径,控制边缘计算只运行在部分路径下。
-
边缘计算路由开关:边缘计算中通过读取路由配置,控制只有部分页面走流式渲染,否则请求直接走动态加速获取完整页面内容。
-
dns 开关,如出现 cdn 严重问题,直接 dns 回切到统一接入。
-
如果边缘计算基础功能出现异常,在 cdn 配置平台上关闭所有路径的边缘计算,走默认的动态加速。
-
如果在进了边缘渲染,在没有返回任何响应内容给客户端前,就出现了错误,捕获错误并降级到获取完整页面内容。
-
如果进了边缘渲染,已经返回了静态部分的响应给客户端,然后在边缘节点了加载动态内容出了问题(超时、http 错误码、与静态内容版本号不匹配),返回一个 location.reload() 的 script 标签,并结束响应,让页面强制刷新。刷新时可带上 bypass 边缘计算的 query 参数以保证刷新时不走边缘渲染。
-
支持类 service worker 环境的边缘计算,功能满足需求。
-
海外节点目前还有限,部分区域性能可与akamai 对标甚至超过,但有些域名性能因节点少的原因还是比 akamai 稍差。
-
只支持简单的请求改写计算,不满足边缘渲染的需求。
-
ESI 可以组装动态和静态内容,但不支持流式,动态内容会阻塞首屏。
-
海外节点多,在一些地区下相比于 alicdn 有性能优势。
-
支持类 service worker 环境的边缘计算,功能满足需求。
-
没有使用经验,如果要用的话可能流程比较复杂。
-
ttfb 减少 1s
-
白屏时间减少 1s
-
核心内容展示时间减少 500ms
参考
[1]cloudfare edge worker (https://blog.cloudflare.com/introducing-cloudflare-workers/) [2]2016 - the year of web streams (https://jakearchibald.com/2016/streams-ftw/) [3]ESI(https://www.w3.org/TR/esi-lang/) [4]Async Fragments: Rediscovering Progressive HTML Rendering with Marko(https://tech.ebayinc.com/engineering/async-fragments-rediscovering-progressive-html-rendering-with-marko/) [5]The Lost Art of Progressive HTML Rendering (https://blog.codinghorror.com/the-lost-art-of-progressive-html-rendering/)
免费下载
优酷前端业务场景多、技术栈繁杂,对前端工程能力的要求越来越高。阿里文娱将团队遇到的技术挑战以及解决过程做详细的展开,希望由解决方案的推演抽丝剥茧,一探优酷前端团队在支撑业务过程中的技术思考和沉淀,为读者带来一些启发。
识别下方二维码,或点击文末“阅读原文”立即下载:
推荐阅读
文章评论