阿里妹导读:当前几种常见的前端性能优化方案仍然不可避免地会存在一些缺点。本文在 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/)
免费下载
优酷前端业务场景多、技术栈繁杂,对前端工程能力的要求越来越高。阿里文娱将团队遇到的技术挑战以及解决过程做详细的展开,希望由解决方案的推演抽丝剥茧,一探优酷前端团队在支撑业务过程中的技术思考和沉淀,为读者带来一些启发。
识别下方二维码,或点击文末“阅读原文”立即下载:
推荐阅读