Serverless 云开发高阶应用

374 阅读8分钟

Serverless 系列文章:

后端架构的演进之路:Serverless 的诞生

Serverless 不代表真的无服务了。

腾讯云 Serverless 实战演练

Serverless 开发实战之 Todo 案例

前面我们在应用中创建了一个云函数,并将云函数与 Express 进行整合,配合云数据库写好了增删改查的接口,但是这样的开发方式并不是 Serverless 的最佳食用方法

在我们的代码中,是将整个后端应用的全部业务能力写进了一个函数中。这样做的好处就是方便管理,毕竟在一个应用下只有一个云函数。但是单个云函数的并发是有限的,并行的函数实例个数由云厂商决定,而超过限制后,事件队列就需要等待其他函数实例执行完毕后,再生成新的函数实例。

那可能就有人问了,不是说 Serverless 是弹性伸缩的吗?不是说会根据业务处理的需求自动调配资源嘛?为什么还会有函数的并发限制呢?

要搞清楚这一点,我们需要了解 FaaS 的运行机制

FaaS 的运行机制

在 FaaS 平台中,函数默认是不运行的,也不会分配任何资源,甚至 FaaS 中都不会保存函数代码。只有当 FaaS 接收到触发器的事件后,才会启动并运行函数。前面我们就是使用 HTTP 的触发器来执行函数代码的,整个函数的运行过程实际上可以分为四个阶段

**代码下载:**FaaS 平台本身不会存储代码,而是将代码放在对象存储中,需要执行函数的时候,再从对象存储中将函数代码下载下来并解压,因此 FaaS 平台一般都会对代码包的大小进行限制,通常代码包不能超过 50MB。

**启动容器:**代码下载完成后,FaaS 会根据函数的配置,启动对应容器,FaaS 使用容器进行资源隔离。

**初始化运行环境:**分析代码依赖、执行用户初始化逻辑、初始化入口函数之外的代码等。

**运行代码:**调用入口函数执行代码。

当函数第一次执行时,会经过完整的四个步骤,前三个过程统称为**“冷启动”,最后一步称为“热启动”**。

整个冷启动流程耗时可能达到百毫秒级别。函数运行完毕后,运行环境会保留一段时间,这个时间在几分钟到几十分钟不等,这和具体云厂商有关。如果这段时间内函数需要再次执行,则 FaaS 平台就会使用上一次的运行环境,这就是**“执行上下文重用”,也叫做 “实例复用”,函数的这个启动过程也叫“热启动”**。“热启动” 的耗时就完全是启动函数的耗时了。当一段时间内没有请求时,函数运行环境就会被释放,直到下一次事件到来,再重新从冷启动开始初始化。

考虑下面这个云函数:

在第一次调用该云函数的时候,函数返回值为 1,这是符合预期的。

但如果连续调用这个云函数,**其返回值有可能是从 2 递增,也有可能变成 1,**这便是实例复用的结果:

  • 当热启动时,执行函数的 Node.js 进程被复用,进程的上下文也得到了保留,所以变量 i 自增。

  • 当冷启动时,Node.js 进程是全新的, 代码会从头完整的执行一遍,此时返回 1。

下面是一个函数的请求示意图,其中 “请求1” “请求3” 是冷启动,“请求2” 是热启动。

函数执行完毕后销毁运行环境,虽然对首次函数执行的性能有损耗,但极大提高了资源利用效率,只有需要执行代码的时候才初始化环境、消耗硬件资源。并且如果你的应用请求量比较大,则大部分时候函数的执行可能都是热启动。

从函数运行的生命周期中你可以发现,如果函数每分钟都执行,则函数几乎都是热启动的,也就是会重复使用之前的执行上下文。执行上下文就包括函数的容器环境、入口函数之外的代码。

云平台会根据当前负载情况,自动控制云函数实例的数量,并且均衡地分发请求。连续的多次请求有可能由同一实例处理,也可能不是。这就是我们在上面的代码中看到的,i 的值非常放肆,根本就找不到规律。所以,我们在编写云函数时,应注意保证云函数是无状态的、幂等的,即当次云函数的执行不依赖上一次云函数执行过程中在运行环境中残留的信息。

再次回到我们的 Todo 案例中,因为我们将全部的业务逻辑放到了一个云函数中,因此,处理的并发量会受到极大的限制。当并发量达到一定的程度时,无法创建更多的函数实例,也就无法分配更多的服务器资源。更好的方式是对我们的业务逻辑进行拆分一个云函数就对应一个独立的业务逻辑处理。这在小程序的云开发中就有体现,默认给我们的小程序云开发模板中,就是一个小程序应用对应对个云函数的处理方式。

那么,随着功能和业务的增加,函数必然会越来越多,对于开发过程中的代码管理工作,也就显得格外重要了。而腾讯官方提供的 cloudbase-framework 工具则给我们提供了一种方式,我们前面使用的 CloudBase CLI 命令行工具,就是使用 cloudbase-framework 的对外接口工具,也就是说,我们使用的命令行,实际就是调用了 cloudbase-framework 提供的功能。

前面我们已经使用过一些了,比如:tcb new 创建应用、tcb 应用部署、tcb service create 创建 http 触发器、tcb fn code update xxx 函数名增量更新代码。

除了这些部署代码相关的命令,framework 还给我们提供了一站式管理云平台资源的能力。

使用 CloudBase Framework

**接下来,我们按照 Serverless 的开发模式对 Todo 案例进行重构。**在腾讯云开发 CloudBase 下,已经给我们创建好了各种各样的开发的模板,使用 tcb new 这个命令就可以看到,在选择应用模板时,选择 Vue 应用,就可以创建一个 Vue 云开发的项目。

项目创建后之后,我们能看到项目路径下有 cloudfunctions 目录,这就是存放云函数的地方,一个函数就是一个文件夹。

那么怎么管理这些函数呢?在项目的根路径下,有一个 cloudbaserc.json 的文件,它就是整个应用的 framework 的配置文件,我们可以通过这个配文件来管理我们的项目应用。所以在开始之前,我们要先来认识一下这个配置文件中,各个配置项的含义:

**version 字段:**CLI 0.9.1+ 版本引入了 2.0 新版本配置文件,支持了动态变量的特性。

在 cloudbaserc.json 中声明 "version": "2.0" 即可启用新的特性,新版配置文件只支持JSON 格式。动态变量特性允许在cloudbaserc.json 配置文件中使用动态变量,从环境变量或其他数据源获取动态的数据。使用 {{}} 包围的值定义为动态变量,可以引用数据源中的值。

**envId 字段:**应用 ID

**$schema :**配置模板的描述信息

**region:**应用所在地区

**framework :**配置文件的主要配置项

framework 字段:name 属性是应用名字

framework.plugins:这是我们管理应用的重点。

Framework 是支持插件机制的,提供了多种应用框架和云资源的插件。应用依赖哪些插件,都在 plugins 参数下配置,framework 会根据 plugins 的配置来管理应用,处理应用中的构建、部署、开发、调试等流程,一个应用可以使用多个插件,使用不同的自定义属性名字进行管理。

官方提供的插件有很多,具体可以查看:docs.cloudbase.net/framework/p…

云函数插件

首先我们对之前写好的云函数进行插件方式的修改:

配置完成后,修改代码,然后进行部署测试。

静态网站插件

云函数配置好之后,回到我们的客户端代码中,正常的开发部署流程是:

在云开发中,有一个静态站点托管的服务,我们可以借助静态网站插件,一键完成打包上线部署的全部工作,不用再手动完成了。

配置也很简单: