正文
本文发表于
北斗同构github
, 转载请注明出处
注: 本文为
第12届D2前端技术论坛《打造高可靠与高性能的React同构解决方案》
分享内容,已经过数据脱敏处理。
前言
-
随着React的兴起, 结合Node直出的性能优势和React的组件化,React同构已然成为趋势之一。享受技术福利的同时,直面技术挑战,在复杂场景下,挑战10倍以上极致的性能优化。
什么是同构?
-
一套代码既可以在服务端运行又可以在客户端运行,这就是同构应用。简而言之, 就是服务端直出和客户端渲染的组合, 能够充分结合两者的优势,并有效避免两者的不足。
为什么同构?
-
性能: 通过Node直出, 将传统的三次串行http请求简化成一次http请求,降低首屏渲染时间
-
SEO: 服务端渲染对搜索引擎的爬取有着天然的优势,虽然阿里电商体系对SEO需求并不强,但随着国际化的推进, 越来越多的国际业务加入阿里大家庭,很多的业务依赖Google等搜索引擎的流量导入,比如Lazada.
-
兼容性: 部分展示类页面能够有效规避客户端兼容性问题,比如白屏。
性能数据
性能是一个综合性的问题, 不能简单地断言同构应用一定比非同构应用性能好,只能说合适的场景加上合理的运用,同构应用确实能带来一定的性能提升, 先来看一个线上的案例。
通常来说,网络状况越差,同构的优势越明显,下图是在不同网络状况下首屏渲染时间的一组对比
线上案例
业界生态
除了开源框架,底层方面React16重构了SSR, react-router提供了更加友好的SSR支持等等, 从某种程度上来说,同构也是一种趋势,至少是方向之一。
思考 与 实现
同构的出发点不是 “为了做同构,所以做了”, 而是回归业务,去解决业务场景中SEO、首屏性能、用户体验 等问题,驱动我们去寻找可用的解决方案。在这样的场景下,除了同构本身,我们还需要考虑的是:
-
高性能的 Node Server
-
可靠的 同构渲染服务
-
可控的 运维成本
-
可复用的 解决方案
-
...
简单归纳就是, 我们需要一个 企业级的同构渲染解决方案。
我们是怎么做的?
基于 eggjs 加入可拔插的同构能力
这里不再赘述具体如何实现,有兴趣的读者可以阅读我们的开源同构框架
beidou
--
github.com/alibaba/bei…
热点问题
任何一种技术都有其适用场景和局限性, 同构也不例外,以下试举一二,以做抛砖引玉.
内存泄漏不是同构应用所特有的,理论上所有服务端应用都可能内存泄漏,但同构应用是“高危群体”, 具体如何解决请参考本人的
《Node应用内存泄漏分析方法论与实战》
, 接下来重点剖析下性能优化。
极致的性能优化
前面也提到了,同构应用并不一定就比非同构应用性能好,影响性能的因素实在太多了,再来看一组数据
上图是基于Node v8.9.1 和 [email protected], 开4个进程采集到的数据, X轴是最终生成页面节点数量,Y轴红色的线表示RT(包括渲染时间和网络时间), 绿色的柱子表示QPS. 可以看出来:
-
随着页面节点的增多渲染时间可能变得很长,QPS下降非常迅速。在页面节点超过3000左右的时候,QPS接近个位数了,而且实际页面中可能包含较复杂的逻辑以及不友好的写法,情况可能会更糟。
顺带提一下, 笔者采样了
淘宝首页
和
淘宝某详情页
以及
Lazada某详情页
,页面节点数分别是2620、2467和3701.
大部分情况下,页面节点数低于1000, 比如
菜鸟物流市场
首页看起来内容不少,其实节点数是775.
那针对3000节点以上的页面,我们该怎么做呢?笔者总结了以下策略并重点阐述其中一两点:
-
采用编译后的React版本: 根据Sasha Aickin的博客,React15在Node4、Node6、Node8下,采用编译后的版本性能相比未编译版本分别提升了2.36倍、3倍、3.85倍
-
模块拆分: 模块拆分有利于并发渲染,目前ICBU店铺装修采用的就是这种方式
-
模块级别缓存: 页面中某些模块其实是很适合缓存的,比如Lazada详情页中节点数虽然高达3701, 但其实页头部分就占比55.5%,页尾占比3.5%,而页头页尾是常年不变的.
-
组件级缓存: 最小粒度的缓存单位了,性能提升依赖于缓存的范围和命中率,运用得当,可能带来非常大的性能提升。参考
walmartlabs
-
采用hsf代替http对外提供服务: hsf的网络消耗远低于http, 在店铺同构实践中,改用hsf, java端调用Node端的耗时缩短了一半.
-
部分模块客户端渲染(对SEO无用的部分): 直接降低SSR部分的复杂度
-
智能降级: 当流量暴增,接近或超过阈值时,会直接导致服务的RT快速上升。可以实时监测CPU和内存的使用率,超过一定的比例自动降级为客户端渲染,降低服务端压力,CPU和内存恢复常态时,自动切回服务端渲染。
-
采用Node8: 同样在店铺实践中,采用Node8相比Node6, 渲染时间从28ms降低到了18ms, 提升幅度为36%.
-
采用最新版React16:
facebook官方数据
, 在Node8下,React16相比编译后的react15仍有3.8倍提升,相比未编译的React15更是有数量级的提升。
组件级缓存
如果说性能优化有"万能"的招式,那一定是缓存, 从Nigix缓存到模块级缓存到组件级缓存,其中最让人兴奋的就是组件级缓存,让我们一起来看看如何实现