前端新思路:组件即函数和Serverless SSR实践

导读:在今天,对于 Node.js 运维和高并发依然是很有挑战的,为了提效,将架构演进为页面即服务,可是粒度还不够,借着云原生和 Serverless 大潮,无运维,轻松扩展,对前端是极大的诱惑。那么,基于 FaaS 之上,前端有哪些可能性呢?

本次分享主要围绕 Serverless SSR 和它的演进过程、背后思考为主。(长文预警,内含超多干货,请耐心阅读)

文末福利:《2020 前端工程师必读手册》电子书已上线,可免费下载。

2019年上半年,我在阿里经济体前端委员会推进的Serverless研发体系共建项目中负责Serverless SSR的研究,将CSR,SSR,边缘渲染进行整合和尝试,提出组件即服务的概念(Component as Service),试图结合FaaS,做出更简单的开发方式。

本文是狼叔在D2大会的分享《前端新思路:组件即函数和Serverless SSR实践》的内容,阅读需要10分钟,你将了解如下内容:

1.可以了解Serverless时代端侧渲染面临的具体问题 

2.可以了解Serverless SSR规范以及渲染体系的完整工作链路和原理 

3.为业内提供解决Serverless SSR渲染问题的新思路

狼叔(网名i5ting)现为阿里巴巴前端技术专家,Node.js 技术布道者,Node全栈公众号运营者,曾就职于去哪儿、新浪、网秦,做过前端、后端、数据分析,是一名全栈技术的实践者。目前负责BU的Node.js和基础框架开发,已出版《狼书(卷1) 更了不起的Node.js》。

技术趋势分析

今年的技术趋势,我的判断是技术混乱期已过,提效才是今日的挑战。

在Node.js领域,今年新东西也不多,最新已经发布到13,lts是12,Egg.js的生态持续完善,进度也不如前2年,成熟之后创新就少了。在很多框架上加入ts似乎已经政治正确了。比如自身是基于ts的nest框架,比如阿里也开源了基于Egg生态的midway框架,整体加入ts,类型系统和oop,对大规模编程来说是非常好的。另外GraphQL也有很强的应用落地场景,尤其是Apollo项目带来的改变最大,极大的降低了落地成本。已经用rust重写的deno稳步进展中,没有火起来,但也有很高的关注度,它不会替代Node.js,而是基于Node之上更好的尝试。

今日的Node.js存在的问题是会用很容易,做到高可用不容易,毕竟高可用对架构运维要求更好一些,这点对前端同学要求会更难一些。做到高可用之后,做性能调优更难。其实,所有这些难点的背后是基于前端工程师的角度来考虑的,这也是非常现实的问题。

对于很多团队,上Node是找死,不上Node是等死。在今天web框架已经相当成熟,所以如何破局,是当下最破解需要解决的问题。

你可能会感觉Node.js热度不够,但事实很多做Node.js的人已经投身到研发模式升级上了。对于今天的Node.js来说,会用很容易,但用好很难,比如高可用,性能调优,还是非常有挑战的。我们可以假想一下,流量打网关,网关根据流量来实例化容器,加载FaaS运行时环境,然后执行对应函数提供服务。在整个过程中,不许关心服务器和运维工作,不用担心高可用问题,是不是前端可以更加轻松的接入Node.js。这其实就是当前大厂前端在做的前端基于Serverless的实践,比如基于FaaS如何做服务编排、页面渲染、网关等。接入Serverless不是目的,目的是让前端能够借助Serverless创造更多业务价值。

为何如此钟爱 SSR?

在2017年底,优酷只有passport和土豆的部分页面用Node.js,QPS不高,大多是一些尝试性业务,优酷PC 和H5核心页面还都是PHP模板渲染。最近2年基于阿里强大的技术体系,我们也对PC、H5多端进行了技术改造。今年双十一是React SSR第一次扛双十一,具有一定意义,背景就不赘述了,参见 看优酷 Node 重构之路,Serverless SSR 未来可期 。

  • 将优酷C端核心页面全部用Node重写,完成了PHP到Node.js的迁移。在没有PHP同学的情况下,前端可以支撑业务。

  • 性能提升明显,从v1(Bigpipe+jQuery)到v2(React SSR),性能逐步提升。PC页面首屏渲染降到150ms、播放器起播时间从4.6秒优化到2秒。H5站上了React SSR后,性能提升3倍,H5唤端率提升也极其明显,头条短视频唤端率由5.68%提升到9.4%,环比提升65%。单机性能qps从80提升150+(压测最高可以到300左右)。

  • QPS过万,2年没有p4以上故障,相对来说是比较稳定的。扛过双十一、世界杯,最高三倍以上的流量。

  • 在集团前端委员会承担Serverless SSR专项。

裕波曾经问我,为何如此钟爱SSR?

从前端的角度看,它是一个相对小的领域。PC已经非主流,H5想争王者,却不想被rn、weex中间截胡。怎么看,SSR能做的都有限。但是,用户体验提升是永远的追求,另外web标准化是正统,在二者之间,和Node做结合,除了SSR,目前想不到更好的解法。

贴着C端业务,从后端手里接过来PC、H5,通过Node构建自己的生存之地是必然的选择。

活下来之后就开始有演进,沉淀,通过C端业务和egg-react-ssr开源项目的沉淀,我们成功的打通2点:

  1. 写法上的统一:CSR和SSR可以共存,继而实现二种模式的无缝切换

  2. 容灾降级方案:从Node SSR无缝切换到Node的CSR,做到第一层降级,从Node CSR还可以继续降到CDN的CSR

2019年,另外一个风口是Serverless,前端把Serverless看成是生死之地,下一代研发模式的前端价值证明。那么,在这个背景下,SSR能做什么呢?基于FaaS的SSR如何呢?继续推演,支持SSR,也可以支持CSR,也就是说基于FaaS的渲染都可以支持的。于是和风驰商量,做了Serverless端侧渲染方向的规划。

本来SSR是Server-side render,演进为Serverless-side render。元彦给了一个非常好概念命名,Caaf,即Component as a fuction。渲染层围绕在以组件为核心,最终统统简化到函数层面。

在今天看,SSR是成功的,一个曾经比较偏冷的点已经慢慢变得主流。集团中,基于React/Rax的一体化开发,可以满足前端所有开发场景。优酷侧的活动搭建已经升级到Rax1.0,对外提供SSR服务。在uc里,已经开始要将egg-react-ssr迁移到FaaS上,代码已经完成迁移。

  • PC/中后台,React的CSR和SSR

  • 移动端/H5,Rax的CSR和SSR。尤其是Rax SSR给站外H5提供了非常好的首屏渲染时间优化,对C端或活动支持是尤其有用的。

在2020年,基于FaaS之上的渲染已经获得大家的认可。另外大量的Node.js的BFF应用已经到了需要治理的时候,BFF感觉和当年的微服务一样,太多了就会牵扯到管理成本,这种情况下Serverless是个中台内敛的极好解决方案。对前端来说,SSR让开发变得简单,基于FaaS又能很好的收敛和治理BFF应用,结合WebIDE,一种极其轻量级基于Serverless的前端研发时代已经来临了。

Serverless-side render 概念升级

从BFF到SFF

了解SSR之前,我们先看一下架构升级,从BFF到SFF的演进过程。

BFF即 Backend For Frontend(服务于前端的后端),也就是服务器设计 API 时会考虑前端的使用,并在服务端直接进行业务逻辑的处理,又称为用户体验适配器。BFF 只是一种逻辑分层,而非一种技术,虽然 BFF 是一个新名词,但它的理念由来已久。

在Node.js世界里,BFF是最合适的应用场景。常见的API、API proxy、渲染、SSR+API聚合,当然也有人用来做网关。

从Backend For Frontend升级Serverless For Frontend,本质上就是利用Serverless基建,完成之前BFF的工作。那么,差异在哪里呢?

核心是从Node到FaaS,本质上还是Serverless,省的其实只是运维和自动扩缩容的工作,一切看起来都是基建的功劳,但对于前端来说,却是极为重大的痛点解决方案,能够满足所有应用场景,基于函数粒度可以简化开发,乃生死必争之地。

SFF 前后端分工

Serverless简单理解是FaaS+BaaS。FaaS是函数即服务,应用层面的对外接口,而BaaS则是后端即服务,更多的是业务系统相关的服务。当下的FaaS还是围绕API相关的工作为主,那么,前端如何和Serverless绑定呢?

Serverless For Frontend(简称SFF)便是这样的概念,基于Serverless架构提供对前端开发提效的方案。

下面看一下SFF分工,这张图我自认为还是非常经典的。首先将Serverless劈成2半,前端和后端,后端的FaaS大家都比较熟悉了,但前端页面和FaaS如何集成还是一片待开发的新领域。

举个例子,常见BFF的例子,hsf调用获得服务端数据,前端通过ctx完成对前端的输出。这时有2种常见应用场景

  1. API,同后端FaaS(RPC居多)

  2. 页面渲染(http居多)

基于FaaS的页面渲染对前端来说是必须的。从beidou、Next.js、egg-react-ssr到Umi SSR,可以看出服务端渲染是很重要的端侧渲染组成部分。无论如何,React SSR都是依赖Node.js Web应用的。那么,在Serverless时代,基于函数即服务(Functions as a Service,简写为FaaS)做API开发相关是非常简单的:

  • 1)无服务,不需要管运维工作

  • 2)代码只关系函数粒度,面向API变成,降低构建复杂度

  • 3)可扩展

目前还是Serverless初期,大家还是围绕API来做,那么,在FaaS下,如何做好渲染层呢?直出HTML,做CSR很明显是太简单了,对于React这种高级玩法如何集成呢?

其实我们可以做的更多,笔者目前能想到的Serverless时代的渲染层具有如下特点。

  • 采用Next.js/egg-react-ssr写法,实现客户端渲染和服务端渲染统一

  • 采用Umi SSR构建,生成独立umi.server.js做法,做到渲染

  • 采用Umi做法,内置Webpack和React,简化开发,只有在构建时区分客户端渲染和服务端渲染,做好和CDN如何搭档,做好优雅降级,保证稳定性

  • 结合FaaS API,做好渲染集成。为了演示Serverless下渲染层实现原理,下面会进行简要说明。在Serverless云函数里,一般会有server.yml作为配置文件,这里以lamda为例子。

SSR 概念升级

现状:

  1. 现有FaaS主要是针对API层做扩展,视图渲染是一个新的命题。

  2. 竞品Serverless.com提供了Components类似的视图渲染层方案

  3. 如何打造一个基于阿里技术栈又有业界领先的端侧渲染解决方案

业界还缺少最佳实践,这是极好的机会。因此,我们对SSR做了概念上的升级(感谢justjavac大佬的提示)

Serverless端渲染层,是针对 SSR 做概念和能力升级:

  • SSR 从 Server side render 升级为 Serverless side render,基于FaaS环境,提供端侧页面渲染能力。

  • Serverless渲染层涵盖的范围扩展,从服务器端渲染升级到同时支持 CSR 和 SSR 2种渲染模式。

在 Serverless 背景下,页面渲染层包含2种情况:

  • 基于 FaaS 的客户端渲染

  • 基于 FaaS 的服务器端渲染

目标是提供基于 FaaS 的页面渲染描述规范,提供标准化组件描述,统一组件写法,用法简单,易实现,可扩展。因此,我们制定了SSR-spec规范,下面会详细讲解。

Serverless-side render 规范和实现原理

在讲规范之前,我们先简单了解3个术语:

CSR 和 SSR

先科普一下CSR和SSR的概念。

客户端渲染(简称CSR),简单理解就是html是没有被动态数据灌入的,即所谓的静态页面。比如通过React、Rax编写的组件,打包构建后,以html文件形式分发到CDN上,不需要Node.js支持,就是非常典型的CSR。

资源加载完成后(注意:只有一个bundle,一次性吐出),通过React中的render API进行页面渲染。优点是不需要服务端接入,简单,对于性能要求不高的页面是非常合适的。中后台应用大多是CSR,优化也都是打包环节玩。

服务器端渲染(SSR),简单理解就是html是由服务端写出,可以动态改变页面内容,即所谓的动态页面。早年的php、asp、jsp这些Server page都是SSR的。但基于React技术栈,又有些许不同,server bundle构建的 时候,要吐多少模块,是server端决定的。client bundle和之前一样,差别在于这次是hydrate,而非render。

hydrate是 React 中提供在初次渲染的时候,去复用原本已经存在的 DOM 节点,减少重新生成节点以及删除原本 DOM 节点的开销,来加速初次渲染的功能。主要使用场景是服务端渲染或者像prerender等情况,所以在图中hydrate之后才是tti时间。

如果想全局了解CSR和SSR,共分5个阶段,参考下图。

纯服务端渲染和纯客户端渲染是2个极端,React SSR是属于中间的,这种服务端吐出的粒度是可以根据业务来控制的,可以服务端多一点,性能会差,也可以服务端吐出刚好够首屏的数据,其他由客户端来处理,这种性能会好很多。Static SSR和预渲染的CSR也是特定场景优化的神器。

最佳写法

了解了CSR和SSR的区别,下面我们看一下最佳写法是如何演进的。业内最好的实现大概是next.js了,抛开负责度不谈,单就写法来说,它确实是最合理的。

既然是基于React做法,核心肯定以Component为主,在Component上扩展静态方法用于接口请求是很好的实践。

写法如下:

早年写过bigpipe相关事项,其中模块成为biglet,它的作用是获取接口,结合tpl生成html。

biglet的生命周期如下。

before
.then(self.fetch.bind(self))
.then(self.parse.bind(self))
.then(self.render.bind(self))
end

fetch是获取接口数据,parse解析数据,最终赋值给data。render是模板引擎编译的函数。每个函数的返回值都约定是promise,便于做流程控制。

很明显,biglet和Component是异曲同工的,而fetch是对象上的方法,必须实例化biglet才能调用,而next的做法getInitialProps是静态方法,不必实例化,内存和复用性上都是非常好的。

这点在egg-react-ssr项目技术选型调研期,我们就已经达成一致了。问题是,next只支持SSR,如何能够更好的支持CSR?做到真正的组件级别的同构。于是,基于这种写法,通过高阶组件进行包装,轻松实现了CSR,核心代码如下。

既然写法上统一了,那么,我们还能进一步进行优化么?核心点在网络获取部分。我们能看到的gRPC-web或isomorphic-fetch,分别实现了:1)RPC和http的约定,2)CSR和SSR中fetch统一。给我们带来的启示是在getInitialProps里,我们还可以做更多同构的玩法。

在webpack打包构建server bundle了的时候会注入isBrowser变量。

const plugins = [
  new webpack.DefinePlugin({
    '__isBrowser__': false //eslint-disable-line
  })
]

以此来区分CSR和SSR做同构兼容就更简单了。

Page.getInitialProps = async (ctx) => {
    if (__isBrowser__) {
        // for CSR
    } else {
        // for SSR
    }
}

以上做法,都是我们基于 https://github.com/ykfe/egg-react-ssr 提炼出来的实践,这些都是SSR-spec的基础。

标FaaS和 SSR 如何结合

有了egg-react-ssr,确立了最佳写法,接下来就是结合FaaS做好集成。

对比一下,getInitialProps的参数和FaaS函数的参数都有context,这个是非常好的:

  • egg-react-ssr中getInitialProps参数ctx是egg/koa的ctx

  • FaaS函数的参数context挂了各种函数、内存、日志、client相关的信息,把response信息挂上理论上也是可行的,只是处理位置问题,参见 https://docs.aws.amazon.com/lambda/latest/dg/Nodejs-context.html

解法:给 context 扩展SSRRender方法。

为了演示Serverless下渲染层实现原理,下面会进行简要说明。在Serverless云函数里,一般会有server.yml作为配置文件,这里以lamda为例子。

通过这个配置,我们可以看出函数app.server对应的http请求路径是'/',这个配置其实描述的就是路由信息。对应的app.server函数实现如下图:

通过提供ctx.SSRRender方法,读取dist目录下的Page.server.js完成服务端渲染。

核心要点:

  • SSRRender方法比较容易实现

  • 采用类似Umi SSR的方式,将源码打包到Page.server.js文件中

  • 在发布的时候,将配置,app.server函数和Page.server.js等文件上传到Serverless运行环境即可

架构升级四阶段

纵观SSR相关技术栈的演进过程,我们大致可以推出架构升级的4阶段:

  • CSR,很多中后台都是这样的开发的,最常见

  • 其次是阿里开源的beidou,基于egg做的React SSR,这是一个集成度很高的项目,很好用,难度也很大

  • Umi SSR是基于egg-react-ssr上演进出来的,在umi之上,用户不需要关心webpack,但打包后的代码返回的是stream,这点抽象,云谦做的非常到位,对于开发者来说,还是需要自建Node web server的。

  • 在Serverless里,具体怎么玩是需要我们来创造的。

对照上图,说明如下:

  1. 在CSR中,开发者需要关心React和Webpack

  2. 在SSR中,开发者需要关心React、Webpack和Egg.js

  3. 在Umi SSR同构中,开发者需要关心React和Egg.js,由于Umi内置了Webpack,开发者基本不需要关注Webpack

  4. 在Serverless时代,基于FaaS的渲染层,开发者需要关心React,不需要关心Webpack和Egg.js

在这4个阶段中,依次出现了CSR和SSR,之后在同构实践中,对开发者要求更高,甚至是全栈。所有这些经验和最佳实践的积累,沉淀出了更简单的开发方式,在Serverless环境下,可以让前端更加简单、高效。

组件即函数

对于组件写法,我们继续抽象,将布局也拉出来。

function Page(props) {
  return <div> {props.name} </div>
}
Page.fetch = async (ctx) => {
  return Promise.resolve({
    name: 'Serverless side render'
  })
}
Page.layout = (props) => {
    const { serverData } = props.ctx
    const { injectCss, injectScript } = props.ctx.app.config
    return (
      <html lang='en'>
        <head>
          <meta charSet='utf-8' />
          <meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no' />
          <meta name='theme-color' content='#000000' />
          <title>React App</title>
          {
            injectCss && injectCss.map(item => <link rel='stylesheet' href={item} key={item} />)
          }
        </head>
        <body>
          <div id='app'>{ commonNode(props) }</div>
          {
            serverData && <script dangerouslySetInnerHTML={{
              __html: `window.__USE_SSR__=true; window.__INITIAL_DATA__ =${serialize(serverData)}`
            }} />
          }
          <div dangerouslySetInnerHTML={{
            __html: injectScript && injectScript.join('')
          }} />
        </body>
      </html>
    )
}
export default 

layout目前看只有第一次渲染有用,但做Component抽象是可以考虑的。现在是首次渲染模式,以后不排除递归组件树的方式(结合bigpipe可以更嗨),那时layout还是有用的。

这样看来,render、fetch、layout是函数,结合Serverless.yml(f.yml)配置,能否将他们放到配置里呢?

路由由f.yml的配置文件中,所以在f.yml增加render配置扩展,具体如下:

functions:
  home:
    handler: index.handler
    render:
      - Component: src.home.index
      - layout: src.home.layout
      - fetch: src.home.fetch
      - mode: SSR | CSR(默认SSR)
      - injectScript(自己改loyout更好)
          - runtime~Page.js
          - vendor.chunk.js
          - Page.chunk.js
      - injectCSS
          - Page.chunk.css
      - serverBundle: Page.server.js
    events:
      - http:
          path: /
          method:
            - GET
  news:
    handler: index.handler
    render:
      - Component: src.news.index
      - layout: src.news.layout
      - fetch: src.news.fetch
      - mode: SSR | CSR(默认SSR)
    events:
      - http:
          path: /
          method:
            -

元彦提了一个非常好的命名:组件即函数。最贴切不过。

将渲染、接口请求、布局分别拆成独立函数,放到配置文件里。如此做法,可以保证函数粒度的职责单一,对于可选项默认值也更友好。比如没有接口请求就不调用fetch,或者没有布局使用默认布局。

这里再拔高一下,CSR和SSR写法一致了,这种写法可以和FaaS结合,也就是说写法上拆成函数后,前端关心的只有函数写法了,使得面向组件开发更容易。

我们在抽象一下,提炼3重境界。

  • 组件即函数,就是上面将的内容。

  • 页面即函数,对开发者而言,其实页面的概念不大,第一次渲染布局,然后entry执行而已,如果抛开构建和配置细节,页面就是第一个组件,即函数

  • 页面即服务,是我之前提的概念,每个页面对应一个Node服务,这样的好处是避免服务器雪崩,同时可以降低页面开发复杂度。

对于页面即服务来说,一个FaaS函数提供http,天然就是独立服务,在基建侧,网关根据流量决定该服务的容器个数,完美的解决了页面渲染过程的所有问题,也就是我们只需要关注组件写法,组件写法又都是函数。所以,组 件即函数,是Serverless渲染层最好的概括。

下面这张图很好的表达了组件即函数的核心:统一写法和用法。

制定规范之前,定位还是先要想明白的。

  • 在FaaS rutime之上,保证可移植性

  • 通过CSR和SSR无缝切换,可以保证首屏渲染效率

  • 由于面向组件和配置做到轻量级开发,可以很好的结合

统一写法和用法是件约定大于配置的事儿,约定才是最难的,既要保证功能强大,还要写法简单,又要有扩展性。显然,这是比写代码更有挑战的事儿。

SSR 规范

统一写法,上一小结已经讲过了。

接下来就是规范相关的周边,如下图。

构建,扩展,跨平台,以及目录都需要约定。

SSR-spec(https://github.com/ykfe/ssr#specification)规范主要定义 SSR 特性,组件写法、目录结构以及 f.yml 文件扩展的编写规范。目录结构待讨论,例如新增功能的API与删除功能的API理论上应该放在一个project当中,此时应该在src目录下建立不同的文件夹来隔离不同函数的模块。

├── dist // 构建产物
│   ├── Page.server.js // 服务端页面bundle
│   ├── asset-manifest.json // 打包资源清单
│   ├── index.html // 页面承载模版文件,除非想换成传统的直接扔一个html文件部署的方式
│   └── static // 前端静态资源目录
│       ├── css
│       └── js
├── config // 配置
│   ├── webpack.js // webpack配置文件,使用chainWebpackConfig方式导出,非必选
│   └── other // 
├── index.js // 函数入口文件
├── f.yml // FaaS函数规范文件
├── package.json
├── src // 存放前端页面组件
│   ├── detail // 详情页
│   │   ├── fetch.js // 数据预取,非必选
│   │   ├── index.js // React组件,必选
│   │   └── layout.js // 页面布局,非必选,没有默认使用layout/index.js
│   ├── home // 首页
│   │   ├── fetch.js
│   │   ├── index.js
│   │   └── layout.js
│   └── layout
│       └── index.js // 默认的布局文件,必选,脚手架默认生成
└── README.md 

命令用法:

$ SSR build
$ SSR deploy

生成的dist目录结构如下:

- dist
  - funcName
    - static
      - clientBundle.js
      - js
      - css
      - images
    - serverBundle.js

构建命令:

$ SSR build
$ SSR build --spa
$ SSR build hello
$ SSR build hello2

Serverless集成步骤,通用方案集成:

$ SSR xxx
$ Serverless deploy 

这里以Umi为例,开发过程分3个步骤:

  1. 本地源码组件开发,然后通过umi dev完成构建

  2. 如果是线上,可以走线上构建服务(webpack打包),如果需要修改,可以走

  3. 构建后的产物结合FaaS函数,直接发布到Serverless平台上

规范里还有很多点也是有思考的,比如多组件支持是基于bigpipe的方式,首先写入layout布局,然后处理多个组件的组合逻辑,最终res.end即可。另外,组件上如果只有fetch方法,没有render方法也是没有问题的。写法有2种,Component的值是数组,即串行方式。Component的值是对象,即并行方式。限于篇幅,这里就不一一赘述了,参见 https://github.com/ykfe/ssr#specification。

落地实践、性能和未来思考

打包与构建

Umi的实现是非常巧妙的,核心在于构建后的server bundle返回值是stream,解耦了对web框架的依赖。在当时还没有实现更好的,所以以Umi为例。

构建产物,各个文件大小的说明,有2种方式:

  • Node_modules打包到bundle,对FaaS runtime无依赖。

  • Node_modules不打包到bundle,放到FaaS runtime里。

这2种方式打包大小可以接受,性能上第二种会更好一点,但没有差很多。

标快速切换 CSR 还是 SSR

前面说了写法上的统一,在工程实践中,可以在配置里,直接设置type快速切换CSR还是SSR。在公司内部,还可以通过diamand配置下发的方式进行动态控制。

其实SSR-spec规范里,还做了更多扩展:

//检查query的信息或者url查询参数或者头信息
conf.mode = req.query.SSR || req.headers['x-mode-SSR'];

容灾打底方案

简单地说,有3层容灾:

  • Node SSR优先,Node调用hsf。

  • Node CSR通过diamind可以快速切换,html是Node吐出的,前端走MTop请求。

  • 当Node服务挂掉,走CDN上的纯CSR,前端MTop请求。

该流程有以下优点:

  • 构建方式一致

    • 服务端/客户端文件构建方式一致

  • 发布方式一致

    • 发布方式一致,统一发布到 CDN,前端资源可以使用 CDN 加速

  • 无需服务端发布

    • 组件代码变动统一使用 diamond 下发版本号,无需服务端发布

  • 及时生效

    • diamond 配置下发后可及时生效

如图:

性能优化

性能优化的要点:

  • 控制SSR中server端占的比例,性能和体验是鱼和熊掌不可兼得。看业务诉求。

  • 能缓存的尽量缓存,和BFF应用一样。

这里举个例子,对接口字段瘦身,就可以渠道意想不到的效果。

性能对比

优酷PC的React SSR性能很好,从v1(Bigpipe+jQuery)到v2(React SSR),性能逐步提升。PC页面首屏渲染降到150ms、播放器起播时间从4.6秒优化到2秒。H5站上了React SSR后,性能提升3倍,H5唤端率提升也极其明显,头条短视频唤端率由5.68%提升到9.4%,环比提升65%。单机性能qps从80提升150+(压测最高可以到300左右)。

淘宝的Rax SSR也性能优异,以一个带数据请求的真实 Rax SSR 应用为例,性能对比数据显示:WIFI 下, SSR 的首屏呈现时间相比 CSR 提升 1 倍;弱网环境下,SSR 相比 CSR 提升约 3.5 倍。

  • SSR Demo 地址:https://Rax-demo.now.sh/SSR/home

  • CSR Demo 地址:https://Rax-demo.now.sh/CSR/home

未来思考

从用户访问页面流程,具体如下:

要点:

  • 应用网关,肯定是要有的,毕竟要挂域名,反向代理等

  • 页面,已经可以放到FaaS上,FaaS之上的http网关可以满足基本需求,离应用级别的还差很多。

  • API代理,这个基于Node FaaS函数非常容易实现

  • API,调用后端服务提供API,比如访问db等,基于Node FaaS函数也是非常容易实现

FaaS细化到函数粒度,在管理上会有巨大挑战,在架构上也需要重新设计:

  • 具有前端特色的函数管理:比如API、Page等类型,path,域名,甚至是具体的应用设置

  • 页面、组件和网关联动,让开发更简单快速

  • 周边,比如监控,统计,数据等等

这里尝试一张图来表示一下前后端的Serverless时代的分工。

  • 统一接入网关是必须的,主要是处理页面和域名的接入。

  • 对于页面进行抽象,围绕组件和搭建来展开,通过在线|本地构建,最终放到页面托管服务中。有了页面托管,才是万里长城的一小步。

  • 接下对API进行拆分,这也是组件组成里重要的部分。

围绕搭建,可以想象到的是组件和接口的抽象。组件除了智能化我能想到的很少,智能化在计算上也能弹性玩就更有想象力了。对于接口可以再细分:

  • 直接操作表,虽不推荐前端做,但确实是必备能力。

  • 通过配置来生成,即元数据管理,对于已有API进行包装,逻辑编排是非常好的。

  • 如果都不满足,自己基于FaaS函数定制就好了。

搭建本身是提效的,组件和接口都能提效,对于前端的价值是尤其大的。前端Serverless专项里,也是有逻辑编排组的,原因大抵如此。

下面再解释一下页面托管服务实现原理。其实这是页面即服务的升级版,以前每个页面对应一个Node服务,这就导致很多Node服务的运维成本非常高,有了Serverless,依然还是一个页面对应一个FaaS函数,但函数的扩容是Serverless基建做的事儿。也就是说页面托管,其实是基于页面的FaaS函数的管理。垂直到页面管理,一切都自动化,会让开发更简单。

目前每个FaaS函数是可以提供http透出地址的,但这个地址不具备定制能力,所以在页面托管服务之上有一层应用网关是必须的。那么,应用网关和页面页面托管服务之间如何联动呢?

  • 最外层,统一接入网关里做应用管理,每个应用都有对应的域名和子应用网关,二者进行绑定 根据流浪,子应用网关也可以自动扩缩容。

  • 在应用设置里,管理子应用网关包含的path和页面,提供反向代理相关的基础功能即可。

  • 设置完子应用包含的页面之后,系统具备将对应页面同步到子应用网关的能力,并且当页面更新的时候能够自动同步,类似于etcd/consul等服务发现同步功能。

有了这部分设计,开发者只需要关注页面的编写就好了。比较上面的3点配置在系统中并不经常做。

基于上面的设计,目的是提高开发速度,沉淀前端中台,具体好处如下:

  • 中后台能够一定程度的收敛到一起,配置化

  • 页面和系统分离,应变能力更强,结合微前端可以有更多想象力

  • 所有开发聚焦到页面维度,能够更好的提效,比如组件沉淀,智能化,搭建等都可以更专注。

  • 很多后端服务也能够很好的沉淀和复用,这和后端常说的能力地图类似,将BFF聚合管理,简化开发

关于未来,我能想到的是:

  • 组件:写代码或智能化生成

  • 页面:配置出来

  • 系统:配置出来

只需要组件级别的函数的轻量级开发模式里,必然会简化前端开发方式,提供人效,最终实现技术赋能业务的目的。在“大中台,小前台”的背景下,贡献前端应变能力。

总结

在《2019,如何放大大前端的业务价值?》一文中,我曾提过:“前端技术趋于成熟,不可否认,这依然是个大前端最好的时代,但对前端来说更重要的是证明自己,不是资源,而是可以创造更多的业务价值。在垂直领域深耕可以让大家有更多生存空间,但我更愿意认为Serverless可以带来前端研发模式上的颠覆,只有简化前后端开发难度,才能更好的放大前端的业务价值。”

致敬所有为Serverless付出的同仁们!


  福利来了 

重磅下载!

《2020 前端工程师必读手册》

阿里巴巴前端委员会推荐!覆盖语言框架、前端智能化、微前端、Serverless及工程化 5 大热点前端技术方向、10+ 核心实战的前端手册,解锁前端新方式,挖掘前端新思路,尽在此刻,赶紧来先睹为快!

识别下方二维码或点击文末“阅读原文”,下载手册:


你可能还喜欢????????

飞猪 Serverless 体系从无到有,落地10余个业务场景

看优酷 Node 重构之路,Serverless SSR 未来可期

关注「Alibaba F2E」

把握阿里巴巴前端新动向

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值