作者 | 零弌
点击查看视频
大家好,我是零弌,来自蚂蚁集团体验技术部。在蚂蚁工作了三年,负责内部 nodejs 框架 chair 以及 nodejs 版本中间件的实现。在蚂蚁内部 nodejs 主要是用来开发一些 BFF 应用,中台产品和一些小工具。
BFF 应用的主要目的是为了让后端可以更好的为前端服务,在微服务架构下,后端能力会更偏向原子化,如果前端直接使用的话会比较难受,比如说一个页面需要访问多个接口,每个接口又会返回很多前端用不到的字段,又比如说在提交一个表单时,会有复杂的流程,涉及到前置检查,数据拼接,如果在前端直接提交不仅不安全也会很复杂。所以聚合层在微服务架构下非常重要。然而聚合接口会非常多,也会有很频繁的修改,让后端来写会有很高的沟通和联调成本,既然是前端需要使用的接口让前端自己来开发是效率最高的方式了。
中台产品会是一些全栈应用,比如说西湖区最好用的云端知识库语雀,就是一个由 nodejs 开发的中台产品。中台产品主要是提供完整的解决方案,chair 在蚂蚁为前端开发者提供了完整能力,从数据到接口都有完整的功能实现。
在蚂蚁,前端开发者也会使用 chair 去开发一些小工具,比如接口测试平台,压测管理等等,这些小工具往往都是来自于日常工作的创意,实现以后效果都很好,帮助大家都解决了实际的问题。
在蚂蚁工作的三年里,支持了各种各样应用的研发流程,发现除了写代码之外开发者还要关心很多线上的运维问题,这部分的问题非常消耗开发者的精力,一是前端开发者往往对服务器相关的操作不熟悉,二是线上问题往往都很突发,任何时间点都可能出问题,导致开发者的体验不好。因此我们看到了这样的问题,就想通过引入 serverless 的方式来解决。今天和大家分享一下我们的演进路线和具体的实施方案。
BFF 研发/运维成本
在蚂蚁 Chair的研发门槛还是很高的,新入门的同学往往是云里雾里的开始上手,可能本地调用的通的接口在部署之后又不通了。这个问题来自于蚂蚁的架构复杂度,首先简单的介绍一下蚂蚁的三地五中心架构,能做到异地多活是因为在中间件层有非常复杂的路由逻辑。路由逻辑是由蚂蚁特色的单元化方式LDC来约定的,LDC和普通的单元化不同的是 LDC中的单元有不同的类型,比如说全局唯一单元,同城只读单元,普通单元。在前端开发者中彻底理解这套结构和路由逻辑的同学非常少。
除了接口的调用之外,业务的可用性非常重要,除了主流程之外,还需要设计非常多的兜底方案。这些方案里就会使用到很多的中间件,比如说异步操作要通过消息来发送,运维开关要通过动态配置来推送,减少下游调用量、加快接口返回速度需要使用到缓存。这些中间件都是由蚂蚁自研,和社区方案不完全相同,不完全相同就带来了高昂的理解和学习成本。
前端主要开发一些产品和活动,会涉及到不同的投放方式,比如说支付宝端内 h5,小程序,淘宝端内h5,普通浏览器中访问的h5。在支付宝端内都会走无线网关,淘宝和普通浏览器中会走HTTP。除了外部的方案,我们还会开发一些比如说缓存的定时同步,消息订阅后的处理。这些研发模式均不相同,学习成本就会高。
距 Chair 应用的首次提交,已经过去了七年。经过这段时间的发展,已经出现了非常多的 BFF 应用,早期的 BFF 应用已经发展到了非常大的规模,对外的接口数最多的已经有 500+ 了,整个应用的的代码量也非常多,导致应用的维护成本很高。并且一个 bff 应用会涉及到多个团队参与开发,环境独占、发布撞车问题屡见不鲜。造成了开发期的效能下降。
BFF 大量研发工作都是为了活动所服务的,在活动的研发过程中就会涉及到前端页面、组件以及接口聚合的开发,在 BFF 的时期需要在两个独立的应用中进行开发,研发体验上不自然,让人感觉分裂。两个应用也会涉及到两个应用的独立发布,流程上也会更长。
现在蚂蚁线上的 BFF 应用很少有下线的操作,主要原因是一个 BFF 应用中承载了很多的功能,产品、活动的代码混杂在一起,无法梳理。这就造成了项目中无用的代码会越来越多,也使得项目的维护成本居高不下。
在研发完成后,需要对应用进行运维,保证业务持续稳定的提供服务。首先,可观测性非常的重要,如何对线上的服务情况心中有数,就需要有监控的支持。业务指标是开发者最关心的,成功率是否有下跌、接口响应时间是否正常、有没有异常出现,这些是最基本的监控。除了业务指标之外,系统的运行情况也是非常需要关注的,CPU是否有飙高的情况,内存有没有稳定在一定的水位还是有缓慢的上涨,是必须要关注的,这个影响到业务能不能持续稳定的提供服务。
在每个活动上线前,容量是最需要关注的事情,业务方首先提供了需要的业务指标,开发者要对系统不断的进行压测、扩容以确定应用可以满足业务方的要求。在满足了业务需求之后,还需要配置限流,保护下游应用不会因为容量问题而挂掉。压测过程是一件非常麻烦的事情,需要准备压测数据,在业务低谷进行全链路压测,耗时耗力,据统计一个新活动上线,一般都需要两天的时间来进行压测。
除了监控、容量有时还需要对线上应用进行人工的运维,除了可以自动处理的容器替换之外,线上出现了oom,core dump都需要进行人工的分析,确定问题引发的原因是什么,应该如何处理。还有可能线上的容器出现了 hang 住的情况,是需要进行人工的替换。
一个 BFF的上线流程主要就分为准备工作,编码,上线,运维。应用和资源的申请是比较复杂的,需要架构师和 sre进行审批,审批过程需要进行材料准备,不断沟通,才能顺利的完成审批。如果我们想要把大应用拆小,申请流程就非常有必要优化。活动是一个非常高频的场景,如果每个活动都需要应用和资源的申请,那可能大部分的时间都消耗在了这两步上,
系分、编码、验证、应用上线这几步都是必不可少的。如果不改需求的话,这几步应该都会很顺利,不涉及到反复的重复劳动和返工。但是系分、编码的门槛会比较高,首先需要设计出高可用的流程,在非核心链路上都需要兜底设计、核心页面挂了也需要保证业务主流程不挂,其次编码阶段也需要对框架、中间件比较熟悉才能少踩坑,快速的完成工作。我们需要优化的就是简化编码流程、降低上手门槛。
从压测开始就比较机械性了,每个应用需要进行的操作都是类似的,重复性很高,做起来会很枯燥,同时又很耗费精力,这样就导致开发者的幸福度不高。对于serverless的场景,运维操作能砍就砍,理想中的情况是完全不需要运维。
Function 开发
因此,我们经过对于 BFF 研发流程的思考,提出了 SFF 的理念,即 serverless for frontend,希望通过 serverless 的思路为前端研发者提效。立下了两个目标,轻研发、免运维。
而现在 serverless 的代名词几乎就是 function,我们会以 function 的形式为开发者提供崭新的编程界面。
首先我们看一下原有应用的接入层,涉及到很多的场景。比如说外部访问的会有 HTTP、无线网关,内部会有 RPC 调用、消息、集群调度,每一种不同的场景都会有不太相同的编程界面和参数。截图中举了两个例子,一个 HTTP controller,一个集群调度,两者的差距挺大的,http 场景下需要关心 HTTP path,method 参数的位置。集群调度场景需要关心调度类型、频率、参数。
虽然这种方式有非常高的灵活性,可以完全的使用各个场景下所有的特性,比如说 HTTP 可以决定是否是流式响应,把参数放在 query header 或者 body。但是在企业内部,往往不需要这么高的灵活性、内部一般都会总结出一套范式来决定什么场景下一般会采用什么样的方式。因此这么高的灵活性在绝大部分场景下可能都是一个负担。
我们需要的是一个更简化的编码方式,只需要关心业务逻辑,代码是如何编写的,而不用去管业务是以什么样的形式被触发的。
我们来看一下简化后的编程界面是什么样的。首先我们提供了一个配置文件,用来配置项目中提供了哪些 function,function 的名字是什么,可以被什么样的协议触发。这样开发者就完全不用关心协议的细节,比如说 http path 是什么样的,极大的减少了协议的学习成本,可以完全只专注于业务本身。同时一套代码可以使用在多种协议的接入场景中,本来每个场景都需要进行一次适配,有几个协议就要写几套代码,function 的模式下就可以节省下了很多的开发成本,如果需要新增一个协议的接入,只需要在配置文件中新增一个配置即可。
对于代码来说,采用了开发者最熟悉的 function 模式,对比 class 会涉及到类构造,状态的管理,方法调用三步。我们希望开发尽可能的简单,并且无状态,所以会采用最简单的 function。调用上下文,比如说当前用户,访问的 trace 会通过第一个参数 ctx 传递给开发者,这个 ctx 和 koa、egg 的 ctx 均不相同。Koa 的 ctx 是 http 上下文,而 function 的 ctx 是纯粹的业务上下文,不涉及到 http 信息,也不提供 http 相关的操作,这就保证了我们协议的扩展性。协议的细节不会泄漏给开发者,协议也就可以无限的扩展。Egg 的 ctx 扩展性非常的强,框架、插件、业务代码都可以对 ctx 进行扩展,导致了 ctx 非常的庞大和复杂,而 function 的 ctx 上有哪些接口是固定的,由框架来限制,如果有扩展的需求需要对框架进行修改。在总结了蚂蚁的 BFF 开发之后,对于框架的扩展需求其实非常的低,主要都是业务代码的复用。关于这个不在本场分享展开。
如果函数还涉及到其余的参数,会通过 function 其余的参数进行传入,这是最符合开发者直觉的。
BFF 模式下前后端分离的模式在 SFF 中又合并了回来。前后端分离最大的意义是明确前后端的边界,提升前后端各自的研发效率。但是在 BFF 模式下,前后端本来就是同一个人在开发的,在这种场景下,在同一个项目中进行开发自然是效率最高的方式。我们在 SFF 中提供了新前后端研发模式,和 Egg 不同的是去除了模版相关,保留了前端项目的独立性,让前端即可以由 CDN 来托管又可以由 Function 自己托管。就可以适配不同的场景,比如说 2c 的服务,有非常高的访问量,由 CDN 来托管是很合理的。对内的服务,访问量低、并且需要私密性,由 function 自身来托管也是很合理的。
云端一体也不是简单的把两个项目放在同一个仓库里就好了。如果只是把一个前端应用和一个 BFF 应用放在一起,那完全没有意义。我们做了很多的工作来削弱前端应用和BFF 应用的边界,比如在命令行工作中不用特别的区分前端和 bff,dev,test 都只需要执行一个命令即可。在研发流程中也只会感知到一个应用。
在 Function 的开发中,我们提到了希望开发者不关心协议的细节,相对应的我们自然不希望在前端的开发中还需要感知协议的细节。因此我们提供了 OneAPI 的客户端,OneAPI 适配了蚂蚁所有的协议,只需要知道系统、方法就可以进行调用。不用关心是 HTTP 还是 RPC 接口,完全统一了接口的调用方式。
如果前端还是要调用 fetch 或者 jsbridge 来实现和后端的交互,这样前后端的边界就还是在,思路要在 react 和 bff 中不停的切换,我们希望尽可能降低前后端的研发差异、降低上下文切换的成本。OneAPI 提供了很好的解决方案,我们会自动的对 function 的代码进行静态分析,生成 OneAPI 客户端。在前端只需要简单的调用一个方法,即可实现一个 function 的调用,在编码过程中是感知了方法调用而不是 http 请求。OneAPI 就像一座桥,帮助我们跨过前后端边界了。
在 BFF 模式中,一个巨石 BFF 应用完全就是承担了太多,会有很多的活动汇集在一个 BFF 应用中,这个会导致很多的问题。我们来看一下把应用拆小,这些问题是怎么迎刃而解的。
在大应用模式下多个活动并发时,限流就会比较混乱,需要计算所有活动值之和,有时每个活动的限流累计之和甚至会超过应用集群的总体限流值。同时由于没有资源隔离,多个活动直接也可能造成互相影响,比如说单个活动中存在死循环或者内存泄漏的问题,就会影响到其他的活动。如果把应用拆小,每个活动都有自己的 function,每个 function 只需计算自己的限流值。同时应用之间天然就是隔离的,不会有应用之间互相影响的问题,提高了稳定性。
在巨石 BFF 应用中,会有各种各样已经下线的活动代码,程序员是一种很懒的物种,几乎没人会去主动的删除项目中的历史代码,这就导致了历史的代码越堆越多,如果有新的活动又引用了老活动的代码,那是再也没有办法删除了。而小应用就会很清理,活动下线之后,对应的 function 代码就可以下线了。一个项目中不会存在很多无用的代码的问题。
一个 BFF 应用参与的团队很有很多,同时发布的活动也会有很多,如果正好有个活动正在发布、灰度,那其他的活动就被阻塞没有办法上线了,并且发布、灰度的流程会很长、可能有半天到一天,非常影响研发效率。如果是一个小应用,流程都是独立的,自然不会有这样的问题。
在我们的 sff 初期,仅解决了轻研发的问题,再看一下蚂蚁 BFF 打怪升级图,该有的运维操作一样没少。仅仅的轻研发还不足以称之为 serverless,我们还需要解决运维问题。
我认为的 serverless 是这样的。无机器预算,在现有基于容器体系下,上线需要申请资源,扩容需要申请资源,实际在线上运行的容器,有太多超低水位的容器在运行,并且资源占用了开发者太多的精力。如果我们可以做到即用即走,空闲的应用释放资源,需要使用时再进行扩容。实现按量计费,用了多少资源就开出多少的账单,就可以为开发者解决这样的问题。
在应用空闲时把资源回收,也有非常高的收益,不仅资源可以得到复用,开发者也不用再关心系统的运行状况,CPU 高了,内存涨了,下一次新的 function 也不会受到影响。没有一直在线的容器自然不需要运维操作了,开发者真正需要关心的应该是业务才对。
高密度部署
以无机器预算和 No Server 为目标,我们研发了一套高密度部署架构,提供了更轻、更快的运行时。
高密度部署架构会分三个部分为大家介绍:
-
高密度部署,是为了实现高资源利用率,降低线上容器水位低的问题。
-
二层调度,是为了进行实时调度,实现即用即走的目标。
-
极速启动,即用即走意味着极快的启动速度,我们希望在 500ms 内完成应用的启动过程
首先我们来看一下高密度部署,高密度指的是什么?这里的密度是指在相同大小的资源下,部署更多的应用。容器部署模式下,一个应用是以 4C8G 的规格来部署,高密度部署模式下,一个应用是以 0.5C512M 的规格在部署。左右两幅图对应的是相同大小的资源,可以很明显的看到高密度部署的模式下密度高了非常之多。
整体的数据是 16:128,这意味着以相同资源,至少可以部署八倍的应用。这里为什么说至少,是因为我们还有即用即走的特性,意味着我们可以将空闲的应用回收,把回收回来资源让给有需要的应用,就可以服务更多的应用。
我们再来看一下为什么高密度部署可以做到更高的资源利用率。一个应用的开销是 0.5C256M,那我们在 4C8G 的规格在就是一个非常低的资源利用率,但是如果我们的规格是 0.5C512M,资源利用率就一下提升起来了。
高密度部署也就是以超小的规格来进行部署,实现资源利用率的提升。
但是如果直接使用小规格容器来部署会有一些额外的问题。首先因为容器的数量翻了倍数倍, K8S 集群承受的压力将会大上数倍,调度将会变得困难,对于集群中的各组件来说同样会有压力。其次容器并不是仅仅意味着计算资源,还有网络、磁盘的资源。举个例子,在大规模集群下,IP 资源都会紧张,再翻数倍就有可能导致 IP 无法分配。同时对于 kubelet 也会有同样的问题,更多的容器意味着更高的压力。
除了小规格容器之外,容器本身还存在一些问题。在蚂蚁这种复杂环境下,K8S 中有太多的组件要参与调度过程,所以容器调度是一个很慢的操作,完全无法满足我们在 500ms 之内完成启动的需求。其次容器本身启动速度也是很慢的,一个 hello world 的容器启动就要 500ms 以上了,完全没有其他的时间可以留给应用启动。因此我们需要一种更轻更快的调度和启动方式。
Alinode Cloud 恰好提供了一种轻量级的资源调度。alinode cloud fork 进程时可以指定进程可以使用的计算资源,并且 alinode 会负责空闲进程的回收,降低了进程调度的成本。那留给我们的工作也就是进行进程级别的调度,当有一个 function 请求需要服务时,我们找到一个空闲的容器,拉起一个 function 进程即可。
这是 Alinode Cloud 的架构图,由一个主进程和一堆沙盒进程组成。沙盒进程也就是 Function 进程,限定了计算资源,主进程会负责额外的管理工作,文件、网络、ipc 线程池,沙盒管理、流量管理等等。调度器也会直接和主进程交互,来拉起所需的 function。
我们再来看一下二层调度指的是什么,一层调度又是什么?一层调度也就是 K8S 集群的调度,调度出来的结果也就是容器,而二层调度是指在容器内再进行调度,对 function 进程进行管理。这样的模式就可以避免对 k8s 集群造成过多的压力,原来是多少容器,现在还是多少容器,不会有额外的调度压力。除了进程更多之外,也没有更多的资源消耗了。
在我们的架构中,网关收到一个请求之后就会进行一次实时的调度,和容器内的调度器进行通信,拉起 function,将请求转发给 function。
网关不会去感知 function 进程是否存活,仅感知 function 最近调度到的容器。同时网关也会感知容器的资源,能不能拉起更多的 function 进程。因此网关的调度策略就会非常简单,首先查询 function 最近调度过的容器,如果找不到或者该容器无额外的计算资源就会随机找一台有空闲资源的容器进行调度。
而容器内的调度器也很简单,收到一个调度请求后,如果 function 进程还存活则直接返回该进程,如果不存在则和 alinode cloud 进行交互,拉起一个新进程。并且无需进行资源回收,alinode 会负责进行空闲资源的回收,容器内调度器可以感知到这个行为。
有了高密度部署和二层调度之后还没有达到一个生产可用的程度,function 的底层仍然是一个 egg 应用。一般启动速度在 3 到 5 秒之间,这个时间太慢了,会造成 function 的第一个请求完全无法正常响应,用户体验极糟。我们需要在 500ms 内完成启动过程,才能让用户体验勉强接受。
Egg 提供了启动数据指标使我们可以来分析一下启动过程中的性能问题是存在在哪。这里贴出了两个很典型的数据,一个是文件加载,另一个是中间件启动。
Egg 是一个基于约定的框架,在启动过程中会对文件目录进行大量的扫描,来确定文件是否存在并对文件进行加载。Load Config 就是对配置文件的加载,会涉及到框架、插件和应用的配置文件的加载。这里可以看到速度很慢,占用了 400 多毫秒。
中间件的启动过程非常复杂,会涉及到文件读取,网络 IO,中间件与中间件之间还会有互相的依赖。这里的 tr 是蚂蚁的 rpc 中间件。是一个非常典型的例子。rpc 的启动依赖了注册中心,注册中心启动涉及到了本地容灾文件的读取,远程注册中心的建练。rpc 启动的时候需要去注册中心订阅 rpc id,等待注册中心进行推送,获取到推送后再和 rpc 服务建立连接。这个过程非常的漫长。
我们来分析一下为什么文件加载会慢,因为加载过程中会有文件 IO,需要进行目录扫描和文件读取,其实这些文件在服务端都是固定的,我们可以进行优化。其次文件读取完成之后需要交给 V8 进行编译,编译也会消耗很多的 CPU 时间。V8 提供了带缓存的编译接口,并且 node 对其进行了封装,因此我们也可以对编译过程进行优化。
Alinode Cloud 提供了一套 require cache 方案,在启动期间可以 dump 读取文件的热点,并且缓存 js 的编译结果。这封缓存文件就可以在多个环境之间复用。我们劫持了 node 的 require 过程,如果缓存存在则直接从缓存里读取文件、并且使用编译缓存进行 js 的编译。加上了 require cache 之后,效果非常的明显,一个需要 3s 才能启动的 function 直接就加速到了 800ms。
我们再来看一下中间件的问题,中间件的启动过程非常的复杂,有非常重的逻辑,并且互相直接还有依赖,需要顺序的启动。同时中间件服务并不是这么稳定,下游的抖动会很影响应用的启动过程,使得应用的启动速度无法得到保障。看起来只要中间件存在我们的启动速度就无法快起来。
我们从 service mesh 的方案获得了启发,可以把所有的 rpc 客户端放到一个 sidecar 里面去,让这个 sidecar 可以提前启动好,不占用 function 的启动时间。这样 function 的启动过程就完全可控了,只涉及到了 function 自己的启动过程,不会有额外的影响,可以稳定的保持在一个固定的表现上。同时我们把中间件搬到独立的进程之后,还有额外的收获,中间件客户端是内存开销大户,把这些客户端搬走之后 function 进程的内存有了非常明显的下降。
系统架构
聊完高密度部署,二层调度,极速启动几个关键技术点,下面来为大家分享一下我们系统整理的架构,这个系统是如何 run 起来的。
网关是直接对接流量的,因此稳定性非常重要,在网关的设计上完全没有外部依赖。首先网关没有持久化存储,不会有数据库连接问题,第二不依赖下游系统,不会因为下游系统的故障导致系统不可用。部署控制面和注册中心推送的信息均会进行本地容灾,完全满足高可用需求。所以网关可以几乎无限的进行水平扩容,不会因为容量问题而导致不可用。当有容量上的需求是完全可以对网关进行扩容。
同时网关需要有极高的性能,在网络上多了一跳,要尽可能的减少对与响应时间的影响,因此网关在处理请求时都是流式处理,不需要使用大量的内存来记录请求体和响应体,因此内存的开销很低。并且请求处理时仅进行 header 的解析,解析 body 是非常消耗 cpu 的操作,网关所需要的只是 function 的路由信息,例如请求对应哪个 function,因此 header 中的信息就足够了,以此来减少 cpu 的消耗。
网关中触发器,路由,LB 是网关的请求主流程。在触发器中,会将不同协议的请求统一的转换为一个 OneAPI 请求,以便网关内部以及 function 对其进行处理。在路由阶段,需要将一个请求定位到一个 function 中去,进行请求到 function 的路由。
function 的路由逻辑会有多种实现,比如说 function 的灰度发布,需要将 1% 的流量转发到新的 function 中去。或者 function 的 AB 测试,根据客户端的某个字端请求到对应版本的 function 代码中去。这些路由信息会通过部署控制面来进行管理,通过动态配置下发的方式推送到网关上。
到了 LB 阶段也就是需要进行实际的容器路由了。这里也就是刚刚提到的二层调度的具体实现,找到最近调度过的容器或者空闲的容器。
容器架构会比网关架构复杂很多。首先是 function 调度器,需要维护调度过的 function 进程的状态,function 进程是否仍然存活、或者已经被 alinode 销毁这是非常关键的数据。当有一个新的 function 请求到达时,就需要判断 function 进程是否存活,存活的情况就可以进行复用,如果 function 进程已经被销毁,那么就需要拉起一个新的 function 进程。
中间件部分涉及到到对框架的修改,中间件的方法不在是一个普通的 js 方法调用。在 function 中的中间件方法仅仅是一个代理方法,实际的中间件执行是在 eggsidecar 进程中。首先我们需要抽象出一套协议用来进行中间件调用的序列化和返回的序列化,需要支持异常情况的处理,并且还要保证分布式链路上下文的延续,不能切换到 egg sidecar 模式之后就把原来的分布式链路不可用了。然后我们需要实现一套客户端和服务端可以在 function 进程和 egg sidecar 进程中进行通信,需要考虑各种边缘场景下的可用,比如说某个进程异常退出了,响应超时等等。最后我们要将框架内中间件的方法统一的代理到 egg sidecar 进程。
Alinode Cloud 中的沙盒进程是实际的 function 运行进程,他们需要严格的计算资源控制来确保不会相互影响,比如说有个 function 是个调皮的邻居,会逐渐占用大量的内存,如果不加以限制会把整个服务器的内存资源耗尽。所以资源隔离是非常重要的特性,事关整个集群的稳定性,如果一个带有严重内存泄漏的 function 会从一个容器开始慢慢的拖垮整个集群。
我们来完整的看一下一个 function 请求链路。首先每个请求都会先通过网关,触发器来进行协议的统一化,路由实现了 function 的多版本控制,LB 实现了二层调度,用一句话来描述就是将一个请求定位了哪个容器上用哪一份代码来运行。到了容器之后,通过触发器解析出网关提供的信息,使用的代码包是什么,具体的请求是什么。
可以看到整个链路中不再有容器了,开发者就不用在关心容器的 cpu 内存怎么样啦。开发者完全和容器、操作系统解耦了,不用关心 function 的运行环境,专注于 function 的结果即可。并且 function 处于空闲时即可回收,剩下的事情也完全不用开发者操作,一些轻微的内存泄漏完全可以不用再处理了。在集群容量充足的情况下,开发者也无需关心压测、容量问题,只需要配置对应的 function 限流值,在流量上涨阶段自然的会进行扩容,在高峰过去之后,空闲的 function 也会进行回收,最大程度的利用了空闲的资源。实现了我心目中的 serverless,无机器预算、在一个应用上线时不用操心资源,后付费即可。No Server,开发者完全不用接触到服务器相关的事情,用完即走。
总结
在此我们就可以把 蚂蚁 BFF 打怪升级图进化为 蚂蚁 SFF 打怪升级图,众所周知在计算机领域优化一个问题的最好方法就是绕过这个问题。在 SFF 的场景下,我们绕过了资源申请、压测、扩容、限流、服务器运维这些步骤,开发者就完全不用再关心运维问题,解决了大量的重复劳动,极大的提升了研发效能,提升了开发者的工作幸福感。
跨入 BFF 时代的时候,一部分前端开发者掌握了后端开发的能力,解决了聚合层到底属于谁的问题,极大的提升了前端开发者的研发效率扩展了研发边界,开创了属于 nodejs 的 BFF 时代。经过这些年的发展,这些 BFF 系统的维护成本越来越高,让很多前端开发者望而却步,拉低了对于 node 开发的兴趣,逐渐的前端开发者开始分裂成了纯前端开发者,BFF 开发者,新的边界又出现了,这意味着研发效能会逐渐的下降。而我认为 serverless 正是解决这个问题的好办法,nodejs 让前端开发者有了编写服务端程序的能力,serverless 有希望让每个前端开发者都可以零门槛的写起来,只有写的人多了,生态才会健康飞速的向前发展。
????第十五届 D2 前端技术论坛 PPT 集合已放出,马上获取
关注「Alibaba F2E」
回复 「PPT」一键获取大会完整PPT