Serverless 云函数: 云计算发展过程中出现的一种计算资源的抽象,它以云计算平台为基础,为开发者提供业务程序的运行环境,开发者无需关注底层资源分配、扩容部署,代码执行所必要的全部服务由平台提供。
SSR 服务端渲染: 指在服务端将 HTML 渲染到前端,早期常用 php jsp 技术来在服务端生成 HTML,近年来 JS 同构化趋势演进下,逐步出现了在服务端上运行前端 JS 代码进行渲染的方案,如 React、Vue 等主流框架的同构渲染。
前端同学无需关注 SSR 机器的运维、申请、扩容,减少部署运维成本,提高开发效率。
目前腾讯 NOW 直播 IVWEB 团队正逐步将 SSR 业务迁移到腾讯云云函数平台上,精简部署运维成本。
Nginx 配置怎么改才能接入直出,直出失败的话又要怎么接入兜底的静态页面?
一头雾水之时,他看见腾讯云的同事 maxlong 关于 Serverless 架构演进的 PPT….
在介绍 SCF 云函数之前,我们先来 diff 一下传统 IaaS 业务架构和云函数 FaaS 业务架构:
基于虚拟机的业务架构 (IaaS)
而云函数架构是这样的:
基于云函数的业务架构 (FaaS)
阿 J 对比了两者架构之后发现,在基于云函数的业务架构下,开发者无需再关注业务基础层的相关配置,可以集中精力处理业务逻辑的开发,基础层由平台负责维护迭代,只要将我们的直出服务部署上云就可以解决部署直出业务中的运维痛点了。
FaaS 的出现使得服务上云变得容易起来,但是 FaaS 并没有解决「公共基础服务」的问题,而所谓公共基础服务,就是形如对象存储、KV 存储、消息推送这样的基础服务,这个问题最终落到了云服务提供商这里,因此市面上的云服务无一例外地都提供了上面的「公共基础服务」,这样的服务模式叫做 BaaS(Backend as a Service)。
Serverless 直译过来叫无服务器,这里并不是说不需要服务器,而是说开发者不需要关注服务器,这部分由平台维护提供,开发者仅需关注业务逻辑的开发即可。
Serverless 架构下的 App
在这样的一种架构下,开发者无须关注支撑应用服务运行的底层资源,以「函数」的形式承载业务逻辑,以「BaaS 服务」的形式支撑公共服务。
公有云基础设施上的 Serverless 演进
考虑到直出服务的特性,阿 J 认为直出业务十分适合上 Serverless,因此他立马开始了直出上云的预研,做 Serveless SSR 服务,免去运维部署烦恼,减少直出接入成本!
阿 J 认真研究了腾讯云的云函数(Serverless Cloud Function,SCF)发现云函数它可以将我们的业务拆成更细的粒度「函数」,而函数的执行环境开发者不需要关注,由平台负责,以下是阿 J 对云函数执行的理解。
云平台在执行这些「云函数」的过程其实就是在对外提供服务,通常情况下,Serverless 函数会用于「响应 HTTP 请求」,即通过 HTTP 访问事件来触发云函数的执行,如下图所示:
云函数用户请求链路
而「函数的执行」不外乎:入参、上下文、返回值、副作用四个要素,如图所示:
函数执行的四个要素
「入参」:
「上下文」:
「副作用」:
「返回值」:
即 HTTP 响应如 {retcode:0,msg:"ok"}
。
阿 J 还了解到根据一定配置部署完云函数之后,云平台会给你一个 URL,通过访问这个 URL 就可以「触发」对应云函数的执行,得到结果。
云函数发布后,会得到一个 URL,那么这个 URL 要怎么接入我们的业务域名下?
除去前端 webpack 打包之外,对于 Serverless 云函数平台,我们还得在原来的打包产物基础上再做一些操作,其核心在于「打包为 zip 上传到云函数」,如下图所示:
云函数打包图示
从图示中可以看到,打包部署的流程由「CLI 工具」承担,原因是为了提供命令式的部署原语,方便 CI 接入。
方便老业务无缝迁移到云函数直出,解决直出业务的运维痛点。
TSW 很大,压缩后近 20 MB,解压出来大很多,不利于云函数的性能。
除此之外,还要自己去实现 window.REQUEST plug 类似这样的 TSW 全局注入的对象,因为旧有方案也依赖这些全局对象。
原来的方案需要 Koa 监听本地端口才能提供服务,而云函数的出入参是块式的,Koa 的出入参是流式的,因此这里需要处理一下云函数的入参。阿 J 的做法是根据云函数入参来动态构造 http 的 IncomingMessage 和 ServerResponse 的实例,然后透传给 app.callback() 进行直出渲染,最后从 ServerResponse 里取得渲染结果构造为云函数返回值返回。
阿 J 考虑到业务的可用性以及之前链路接入的痛苦,决定接入 NGW(Node Gateway):
NGW 下的 Serverless 直出链路
实现兜底逻辑:
灰度逻辑:
链路日志收归:
长期以来,前端不好查具体的链路信息,现在有了 NGW 一切皆有可能。
云函数的无状态模型使得其非常易于进行本地调试,我们只需要在本地构造函数的入参、上下文即可直接进行直出调试了,阿 J 在实际实现中是通过本地起一个 Koa 服务监听端口,利用这个端口的请求来构造入参、上下文,最后传入函数执行结果,返回到前端显示。
在最后,阿 J 完成了 Serverless 直出方案,其直出过程如下图所示:
「Init」:
「Koa」:
「Clear」:
清理云函数环境、处理 Koa Response 返回直出结果。
特别需要提出的一点是,其中的 Koa 其实就是原方案的打包结果,新方案在此基础上作了一些环境迁移、重写,使其可以在云函数环境下执行渲染。
阿 J 在完成了新直出方案之后马上进行了压测,发现随着压测压力的增加,收包率会出现断崖式下跌,而且还发现部分函数执行耗费时间非常长,联系了云函数的同事看了下发现是「冷启动问题」,那什么是冷启动?
从云函数的架构中可以看到,云函数触发后并不是马上执行,它需要一个环境初始化的过程,这种启动叫做冷启动。还有一种情况是,这次请求的函数执行之后马上接到下一次请求,这时候就不用重新初始化云函数环境,而是直接启动,这种称为热启动。
压测云函数执行时间分布图
收包率优化前后(右图为优化后)
冷启动问题在压力低的情况下不明显,但是在高并发的情况下就会额外影响回包了,这里 SCF 的同事进行了优化:提高最小实例数,减少冷启动,最小实例越多就越能扛住瞬时并发,优化效果如上图右边所示。
此外,我们还发现了内存的问题,这里联系了 SCF 的同事进行了 Node 内存模型的相关优化。优化后,已基本不存在 4xx 的问题。
(业务中的内存使用也可以进行优化,要避免 JS 内存泄漏)
内存超限导致 4xx 问题,压测压力越大,这种错误越多。
内存模型优化后的表现
到这一步,阿 J 终于初步完成了 Serverless 直出方案设计开发,并开始逐步在业务中使用推广。
目前 NGW + Serverless SSR 已经应用到 NOW 直播、手 Q 附近、浏览器直播和手 Q 群送礼等多个项目中。在实际业务开发中,Node 业务的部署和运维工作量降低了 80% 以上。
现在大家经常讨论大前端架构的话题,主要是跨平台开发比较热,小程序兴起、Flutter 火热,在企业追求更高效率和大前端技术快速发展的背景下,对技术性能的要求也越来越高。如果你想了解更多前沿趋势和实践案例,不妨来 GMTC 全球大前端技术大会(深圳站)为自己充电,看一线大厂年终总结。点击 【阅读原文】 或识别二维码直达大会日程,有任何问题欢迎联系票务小姐姐鱼丸:132690780239(微信同号)