27

Midway Serverless 能力介绍与设计分析

 3 years ago
source link: https://mp.weixin.qq.com/s/gTwnFJWMeFdY1snT7wG9ew
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Midway Serverless 能力介绍与设计分析

Original 张挺 Alibaba F2E 8/7
收录于话题
#Midway

这次分享的内容分为两部分, 一块是 Midway Serverless 的能力介绍,第二块是这些能力的设计、思考、沉淀。

Image

Background

640?wx_fmt=jpeg

当前业界的 Serverless 化方向如火突入,有如阿里正在利用 Serverless 将原有业务迁移,降低成本的,也有正在向这些方向努力前进的,我首先会介绍一下当前 Serverless 的一些背景,前端使用 Serverless 的一些场景和方向。第二块会简单介绍一下 Midway Serverless 的一些基础上手和使用。第三块会介绍 Midway Serverless 在抹平平台差异,架构防腐层的一些设计与心得,最后是对未来的一些期望,方向的思考。

Midway 从 2014 年开始一直在集团承担 Node.js 应用的基础开发框架,最开始是 express,到后面的 koa,egg 体系等,将集团业务承载至今。最开始的前后端分离,到如今的函数化,都在不断的开拓前端职能,让业务更聚焦,开发更提效。

之前的 midway v1 版本,我们认为 midway 是一个 Web 全栈框架,提供 Web 服务,增加了依赖注入之后,也适合于大型应用的开发,灵活性和应用的可维护性也得到了验证。

而到了现在 Midway Serverless 时期,整个 Midway 框架的定位在逐步的变化。

640?wx_fmt=jpeg

首先,Midway Serverless 是一个 serverless framework,可以在让代码在多云平台部署,在用户选择时可以减少一些顾虑。

第二是能够方便的让传统的应用迁移上现有的弹性服务,毕竟在集团内,还有非常多的传统应用,不管是在什么场景,这些应用都还需要人维护,需要占用大量的资源,如果能上弹性,对节省成本有非常大的好处。

第三是让应用本身能够在传统应用和函数之间切换,传统的 midway 是基于装饰器加上依赖注入的特性构建出来的,在函数体系下上,也可以这样做,甚至于通过构建将不同的场景结合到一起,我们希望最后能达到代码不变的情况下,不同场景都可用的状态。

结构



640?wx_fmt=jpeg

这是我们经典的目录结构,最简单的,抛开 ts 的一些文件,只需要 f.yml 这个配置以及 index.ts 这个逻辑文件,而复杂一点,也只是增加了目录,增加了不同逻辑的分层,和传统的写法契合。

这里的 f.yml 就承载了之前的路由层的功能,在 Serverless(FaaS)体系中,路由交给了网关处理,那么我们只需要在项目代码中写对应的原 Controller 的内容即可。

640?wx_fmt=jpeg

如图所示,f.yml 中每一个服务都会对应一个接口,每个接口都由一个方法承载由 handler字段去映射绑定,而实际运行中,通过依赖注入的方式,框架只根据当前执行的逻辑动态初始化其中方法,所以也不需要担心执行的性能问题。

640?wx_fmt=jpeg

f.yml 通过标准化适配多云平台,最简单的来说,可以通过定义 http 触发器的 path 和 method 具体的指定接口地址,也可以简化到默认值,自动变为通配路由等等。

640?wx_fmt=jpeg

工具链和能力



除了 f.yml 这套标准定义文件,我们还提供了 faas-cli ,一个精简的本地开发工具,帮助函数体系开发的更好。在开发层面,我们只精简的提供了 createinvoketestdeploy 四个命令,对应了整个研发流程的四个周期,而剩下的部分,则交给了对应平台自身的能力来完成,同时,我们后续也会提供一些后置管理,让 Node.js 开发本身更加的高效。

从 v1.0 之后,我们也提供了一系列示例,不管是和前端集成的 React/Vue,还是场景化示例,博客,Todo list 等等。

640?wx_fmt=jpeg

原理解析



虽然给大家展示了开发的工具链,开发的标准,解释了运行时机制,大家是不是还是很疑惑,依赖注入是如何把 f.yml 中的 handler 字段如何与代码中对应的装饰器连接的,而函数整个原来的参数是如何和云平台对接,做到一套代码跨多平台的?

为了方便理解,我们拿 Midway v1 里的依赖注入容器来解释。

640?wx_fmt=jpeg

整个 Midway v1 是基于 EggJS 往上扩展,增加 IoC(依赖注入) 容器的初始化部分,并且将装饰器的能力注册到其中,和整个路由体系结合到一起。

右边是我们核心的伪代码,在初始化时,容器会做一次扫描,把当前用户的代码都加载到内存中,并分析其中的装饰器组成一个”依赖图“,在每次执行逻辑的时候,从其中拿到对应的实例(get),并将其依赖,子依赖统一初始化。

640?wx_fmt=jpeg

路由部分也是这种逻辑的其中一层,在调用路由时,获取到对应的 Controller key,找到对应的方法,整个 Midway v1 都是如此运行起来的。

在之后的迭代过程中,我们发现这样和单一框架依赖会比较深,很难去灵活的调整功能,并且在 Web 场景的能力,很难去适配到其他场景,这就给逻辑的复用和扩展造成了不少困难。

我们希望不同的场景的代码,能够在一定程度上能够复用,比如常见的 router/orm/graphql 等等,都是可以横跨不同的场景去复用的,甚至于用户的服务层代码本身也是可以去多处复用的。另外一块,我们希望传统全栈到 Serverless 的过程是有延续性的,不希望代码的写法有比较大的区别,既能在不同的平台通用,又能在不同的技术栈大部分通用。

这也迫使我们的去思考不同的代码设计,找到最佳的路径。

Design

640?wx_fmt=jpeg

我们从最原始的函数写法给大家讲起。

架构防腐



整个原始的入口函数,社区的写法都非常简单,是一个传统的方法,其参数在不同平台根据不同触发器略有不同。

640?wx_fmt=jpeg

在执行时,通过网关调度到其内部的运行时,然后拼装参数执行到用户的入口函数中。

为了和之前的框架结合,以及屏蔽不同平台之间的差异,我们在社区的运行时执行之后,用户入口函数之前,做了一层架构抽象,即我们所谓的”防腐层“。这层一共包括两个功能,一是运行时防腐的部分,屏蔽出入参数差异,屏蔽异步差异,错误处理等等。第二部分是 API 传承,将传统的 Midway v1 的容器初始化,根据 yml 里的信息实例化对应的函数方法。

640?wx_fmt=jpeg

这样说有一点抽象,我们找一段实际的代码来理解一下。

下面是实际生成的代码入口 index.js 的示例。

640?wx_fmt=jpeg

初始化的时候,我们会做两个事情,一个就是每个平台的适配器,会自动根据 f.yml 中配置的 provider.name 来生成,我们会自动提供支持的平台启动器(现在已经有阿里云,腾讯云,以及即将完成的 aws)。

另外一个就是 Midway Serverless 框架的入口(FaaSStarter),通过它,来调用到实际的用户代码(src/index.ts)。其余的 asyncWrapper 和 asyncEvent 则是用于对异步函数的包裹,让代码可以统一用上 async 关键字。

640?wx_fmt=jpeg

看到这里,大家是否好奇我们的运行时适配器(防腐层)内部是如何运作的?这就来稍微详细一点的讲讲。

640?wx_fmt=jpeg

整个运行时处在最中间的部分,往上承接事件带来的数据,接收,中转,往下调用到业务代码,把中转的参数传入,在整个容器中占了非常重要的部分 。一般来说,整个运行时包括几个部分,一是语言的 VM,基建的 SDK 等等,比如 Node.js 10/12,日志采集模块等等,二是运维的脚本,用来控制启停,健康检查等等,第三块就是运行时实际代码,简单的实现的话,可以理解起了个 http 服务,并在其中加载业务函数的代码。

640?wx_fmt=jpeg

生命周期



传统的社区平台都会默认埋入自己的运行时,而我们的运行时则是在这些平台内置的运行时之上的封装,并且将运行时和业务代码通过自定义生命周期进行关联,将整个代码 run 起来。

640?wx_fmt=jpeg

整个生命周期分为几部分阶段,外围运行时包括 RuntimeStartFunctionStartInvokeClose 等阶段,而在这些周期中,还提供了 before 和 after 的钩子,方便对这些阶段进行扩展。

我们来一下实际的运行时扩展的例子,看看我们是如何抹平不同的云平台的。

640?wx_fmt=jpeg

这是一个阿里云运行时适配器的例子,我们接着上面的业务代码调用的路径来观察,asyncEvent 用来包裹真实的入参,在接受到参数之后,我们做了一些不同触发器的类型判断,将其分为了 Web 和非 Web 两种类型。

640?wx_fmt=jpeg

在 Web 的处理方法中,进一步细化内容,比如判断是否是网关的类型,构造出一个类似 koa ctx 的结构,处理 body 参数等。

640?wx_fmt=jpeg

做完这些事情之后,就开始把规则化好的参数传递给用户的真正的逻辑了,这个时候,由于生命周期的存在,开始执行才看到的 invoke 过程,并在内部调用 before 和 after 过程。

除此之外,我们还提供了一个默认执行拦截的能力,这个能力在传统应用迁移的时候起了巨大的作用。

应用迁移



640?wx_fmt=jpeg

现在,所有的扩展(Layer)都可以复用在所有的运行时适配上,整个函数体系和应用迁移体系基于这套生命周期和扩展机制,将能力复用,结构分层表现的淋漓尽致。

640?wx_fmt=jpeg

而用户所需要做的,仅仅增加一个 f.yml,写入这 4 行即可。整个 midway 构建器 会生成所有需要的入口和适配代码。

这套函数和应用统一的方案,如果在企业内部,私有化部署也是非常适合的,阿里集团内部的函数体系也是如此被加载起来,和社区保持了高度的一致,也减少了很多的维护成本。

小结

从 midway serverless 的基础到入口的生成,生命周期以及应用迁移的方案介绍,这里涵盖的是 midway serverless 的架构防腐的一小部分,后续也将会有其他文章介绍不同的部分,感谢大家阅读,也欢迎大家关注 Midway。


你可能还喜欢👇👇

640?wx_fmt=jpeg

自研开源框架 Midway Serverless ,让前端提效 50% 背后的故事

640?wx_fmt=jpeg

阿里 Midway 正式发布 Serverless v1.0,研发提效 50%


640?wx_fmt=jpeg

关注「Alibaba F2E」

把握阿里巴巴前端新动向


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK