探索 Serverless 中的前端开发模式

做者: 蒋航
Github: github.com/nodejhjavascript

最近关于 Serverless 的讨论愈来愈多。看似与前端关系不大的 Serverless,其实早已和前端有了渊源,而且将对前端开发模式产生变革性的影响。本文主要就根据我的理解和总结,从前端开发模式的演进、基于 Serverless 的前端开发案例以及 Serverless 开发最佳实践等方面,与你们探讨 Serverless 中的前端开发模式。本人也有幸在 QCon2019 分享了这一主题。html

前端开发模式的演进

首先回顾一下前端开发模式的演进,我以为主要有四个阶段。前端

  1. 基于模板渲染的动态页面
  2. 基于 AJAX 的先后端分离
  3. 基于 Node.js 的前端工程化
  4. 基于 Node.js 的全栈开发

基于模板渲染的动态页面

在早起的互联网时代,咱们的网页很简单,就是一些静态或动态的页面,主要目的是用来作信息的展现和传播。这个时候开发一个网页也很容易,主要就是经过 JSP、PHP 等技术写一些动态模板,而后经过 Web Server 将模板解析成一个个 HTML 文件,浏览器只负责渲染这些 HTML 文件。这个阶段尚未先后端的分工,一般是后端工程师顺便写了前端页面。java

基于 AJAX 的先后端分离

2005 年 AJAX 技术的正式提出,翻开了 Web 开发的新篇章。基于 AJAX,咱们能够把 Web 分为前端和后端,前端负责界面和交互,后端负责业务逻辑的处理。先后端经过接口进行数据交互。咱们也再也不须要在各个后端语言里面写着难以维护的 HTML。网页的复杂度也由后端的 Web Server 转向了浏览器端的 JavaScript。也正因如此,开始有了前端工程师这个职位。node

基于 Node.js 的前端工程化

2009年 Node.js 的出现,对于前端工程师来讲,也是一个历史性的时刻。随着 Node.js 一同出现的还有 CommonJS 规范和 npm 包管理机制。随后也出现了 Grunt、Gulp、Webpack 等一系列基于 Node.js 的前端开发构建工具。mysql

在 2013 年先后,前端三大框架 React.js/Angular/Vue.js 相继发布第一个版本。咱们能够从以往基于一个个页面的开发,变为基于一个个组件进行开发。开发完成后使用 webpack 等工具进行打包构建,并经过基于 Node.js 实现的命令行工具将构建结果发布上线。前端开发开始变得规范化、标准化、工程化。webpack

基于 Node.js 的全栈开发

Node.js 对前端的重要意义还有,以往只能运行在浏览器中的 JavaScript 也能够运行在服务器上,前端工程师能够用本身最熟悉的语言来写服务端的代码。因而前端工程师开始使用 Node.js 作全栈开发,开始由前端工程师向全栈工程师的方向转变。这是前端主动突破本身的边界。git

另外一方面,前端在发展,后端也在发展。也差很少在 Node.js 诞生那个时代,后端广泛开始由巨石应用模式由微服务架构转变。这也就致使以往的先后端分工出现了分歧。随着微服务架构的兴起,后端的接口渐渐变得原子性,微服务的接口也再也不直接面向页面,前端的调用变得复杂了。因而 BFF(Backend For Frontend)架构应运而生,在微服务和前端中间,加了一个 BFF 层,由 BFF 对接口进行聚合、裁剪后,再输出给前端。而 BFF 这层不是后端本质工做,且距离前端最近和前端关系最大,因此前端工程师天然而然选择了 Node.js 来实现。这也是当前 Node.js 在服务端较为普遍的应用。github

下一代前端开发模式

能够看到,每一次前端开发模式的变化,都因某个变革性的技术而起。先是 AJAX,然后是 Node.js。那么下一个变革性的技术是什么?不言而喻,就是 Serverless。web

Serverless 服务中的前端解决方案

Serverless 简介

根据 CNCF 的定义,Serverless 是指构建和运行不须要服务器管理的应用程序的概念。(serverless-overview)

Serverless computing refers to the concept of building and running applications that do not require server management. --- CNCF

其实 Serverless 早已和前端产生了联系,只是咱们可能没有感知。好比 CDN,咱们把静态资源发布到 CDN 以后,就不须要关心 CDN 有多少个节点、节点如何分布,也不须要关心它如何作负载均衡、如何实现网络加速,因此 CDN 对前端来讲是 Serverless。再好比对象存储,和 CDN 同样,咱们只须要将文件上传到对象存储,就能够直接使用了,不须要关心它如何存取文件、如何进行权限控制,因此对象存储对前端工程师来讲是 Serverless。甚至一些第三方的 API 服务,也是 Serverless,由于咱们使用的时候,不须要去关心服务器。

固然,有了体感还不够,咱们仍是须要一个更精确的定义。从技术角度来讲,Serverless 就是 FaaS 和 BaaS 的结合。

Serverless = FaaS + BaaS。

简单来说,FaaS(Function as a Service) 就是一些运行函数的平台,好比阿里云的函数计算、AWS 的 Lambda 等。

BaaS(Backend as a Service)则是一些后端云服务,好比云数据库、对象存储、消息队列等。利用 BaaS,能够极大简化咱们的应用开发难度。

Serverless 则能够理解为运行在 FaaS 中的,使用了 BaaS 的函数。

Serverless 的主要特色有:

  • 事件驱动
    • 函数在 FaaS 平台中,须要经过一系列的事件来驱动函数执行。
  • 无状态
    • 由于每次函数执行,可能使用的都是不一样的容器,没法进行内存或数据共享。若是要共享数据,则只能经过第三方服务,好比 Redis 等。
  • 无运维
    • 使用 Serverless 咱们不须要关心服务器,不须要关心运维。这也是 Serverless 思想的核心。
  • 低成本
    • 使用 Serverless 成本很低,由于咱们只须要为每次函数的运行付费。函数不运行,则不花钱,也不会浪费服务器资源

Serverless 服务中的前端解决方案架构图

上图是当前主要的一些 Serverless 服务,以及对应的前端解决方案。

从下往上,分别是基础设施和开发工具。

基础设施主要是一些云计算厂商提供,包括云计算平台和各类 BaaS 服务,以及运行函数的 FaaS 平台。

前端主要是 Serverless 的使用者,因此对前端来讲,最重要的开发工具这一层,咱们须要依赖开发工具进行 Serverless 开发、调试和部署。

框架(Framework)

现在尚未一个统一的 Serverless 标准,不一样云计算平台提供的 Serverless 服务极可能是不同的,这就致使咱们的代码,没法平滑迁移。Serverless 框架一个主要功能是简化 Serverless 开发、部署流程,另外一主要功能则是屏蔽不一样 Serverless 服务中的差别,让咱们的函数可以在不改动或者只改动很小一部分的状况下,在其余 Serverless 服务中也能运行。

常见的 Serverless 框架有 Serverless FrameworkZEIT NowApex 等。不过这些基本都是国外公司作的,国内尚未这样的平台。

Web IDE

和 Serverless 紧密相关的 Web IDE 主要也是各个云计算平台的 Web IDE。利用 Web IDE,咱们能够很方便地在云端开发、调试函数,而且能够直接部署到对应的 FaaS 平台。这样的好处是避免了在本地安装各类开发工具、配置各类环境。常见的 Web IDE 有 AWS 的 Cloud九、阿里云的函数计算 Web IDE、腾讯云的 Cloud Studio。从体验上来讲,AWS Cloud9 最好。

命令行工具

固然,目前最主要的开发方式仍是在本地进行开发。因此在本地开发 Serverless 的命令行工具也必不可少。

命令行工具主要有两类,一类是云计算平台提供的,如 AWS 的 aws、 Azure 的 az、阿里云的 fun;还有一类是 Serverless 框架提供的,如 serverlessnow

大部分工具如 serverlessfun 等,都是用 Node.js 实现的。

下面是几个命令行工具的例子。

建立
# serverless
$ serverless create --template aws-nodejs --path myService
# fun
$ fun init -n qcondemo helloworld-nodejs8
复制代码

部署
# serverless
$ serverless deploy
# fun
$ fun deploy
复制代码

调试
# serverless
$ serverless invoke [local] --function functionName
# fun
$ fun local invoke functionName
复制代码

应用场景

在开发工具上面一层,则是 Serverless 的一些垂直应用场景。除了使用传统的服务端开发,目前使用 Serverless 技术的还有小程序开发,将来可能还会设计物联网领域(IoT)。

不一样 Serverless 服务的对比

上图从支持语言、触发器、价格等多个方面对不一样 Serverless 服务进行了对比,能够发现有差别,也有共性。

好比几乎全部 Serverless 服务都支持 Node.js/Python/Java 等语言。

从支持的触发器来看,几乎全部服务也都支持 HTTP、对象存储、定时任务、消息队列等触发器。固然,这些触发器也与平台本身的后端服务相关,好比阿里云的对象存储触发器,是基于阿里云的 OSS 产品的存取等事件触发的;而 AWS 的对象存储触发器,则是基于 AWS 的 S3 的事件触发的,两个平台并不通用。这也是当前 Serverless 面临的一个问题,就是标准不统一。

从计费的角度来看,各个平台的费用基本一致。在前面也提到,Serverless 的计费是按调用次数计费。对于各个 Serverless,每月都有 100 万次的免费调用次数,以后差很少 ¥1.3/百万次;以及 400,000 GB-s 的免费执行时间,以后 ¥0.0001108/GB-s。因此在应用体量较小的时候,使用 Serverless 是很是划算的。

基于 Serverless 的前端开发模式

在本章节,主要以几个案例来讲明基于 Serverless 的前端开发模式,以及它和以往的前端开发有什么不同。

在开始具体的案例以前,先看一下传统开发流程。

在传统开发流程中,咱们须要前端工程师写页面,后端工程师写接口。后端写完接口以后,把接口部署了,再进行先后端联调。联调完毕后再测试、上线。上线以后,还须要运维工程师对系统进行维护。整个过程涉及多个不一样角色,链路较长,沟通协调也是一个问题。

而基于 Serverless,后端变得很是简单了,以往的后端应用被拆分为一个个函数,只须要写完函数并部署到 Serverless 服务便可,后续也不用关心任何服务器的运维操做。后端开发的门槛大幅度下降了。所以,只须要一个前端工程师就能够完成全部的开发工做。

固然,前端工程师基于 Serverless 去写后端,最好也须要具有必定的后端知识。涉及复杂的后端系统或者 Serverless 不适用的场景,仍是须要后端开发,后端变得更靠后了。

基于 Serverless 的 BFF

一方面,对不一样的设备须要使用不一样的 API,另外一方面,因为微服务致使前端接口调用的复杂,因此前端工程师开始使用 BFF 的方式,对接口进行聚合裁剪,以获得适用于前端的接口。

下面是一个通用的 BFF 架构。


BFF @ SoundCloud

最底层的就是各类后端微服务,最上层就是各类前端应用。在微服务和应用以前,就是一般由前端工程师开发的 BFF。

这样的架构解决了接口协调的问题,但也带来了一些新的问题。

好比针对每一个设备开发一个 BFF 应用,也会面临一些重复开发的问题。并且以往前端只须要开发页面,关注于浏览器端的渲染便可,如今却须要维护各类 BFF 应用。以往前端也不须要关心并发,如今并发压力却集中到了 BFF 上。总的来讲运维成本很是高,一般前端并不擅长运维。

Serverless 则能够帮咱们很好的解决这些问题。基于 Serverless,咱们可使用一个个函数来实各个接口的聚合裁剪。前端向 BFF 发起的请求,就至关因而 FaaS 的一个 HTTP 触发器,触发一个函数的执行,这个函数中来实现针对该请求的业务逻辑,好比调用多个微服务获取数据,而后再将处理结果返回给前端。这样运维的压力,就由以往的 BFF Server 转向了 FaaS 服务,前端不再用关心服务器了。

上图则是基于 Serverless 的 BFF 架构。为了更好的管理各类 API,咱们还能够添加网关层,经过网关来管理全部 API(好比阿里云的网关),好比对 API 进行分组、分环境。基于 API 网关,前端就不直接经过 HTTP 触发器来执行函数,而是将请求发送至网关,再由网关去触发具体的函数来执行。

基于 Serverless 的服务端渲染

基于当下最流行的三大前端框架(React.js/Anguler/Vue.js),如今的渲染方式大部分都是客户端渲染。页面初始化的时候,只加载一个简单 HTML 以及对应的 JS 文件,再由 JS 来渲染出一个个页面。这种方式最主要的问题就是白屏时间和 SEO。

为了解决这个问题,前端又开始尝试服务端渲染。本质思想其实和最先的模板渲染是同样的。都是前端发起一个请求,后端 Server 解析出一个 HTML 文档,而后再返回给浏览器。只不过以往是 JSP、PHP 等服务端语言的模板,如今是基于 React、Vue 等实现的同构应用,这也是现在的服务端渲染方案的优点。

但服务端渲染又为前端带来了一些额外的问题:运维成本,前端须要维护用于渲染的服务器。

Serverless 最大的优势就是能够帮咱们减小运维,那 Serverless 能不能用于服务端渲染呢?固然也是能够的。

传统的服务端渲染,每一个请求的 path 都对应着服务端的每一个路由,由该路由实现对应 path 的 HTML 文档渲染。用于渲染的服务端程序,就是这些集成了这些路由的应用。

使用 Serverless 来作服务端渲染,就是将以往的每一个路由,都拆分为一个个函数,再在 FaaS 上部署对应的函数。这样用户请求的 path,对应的就是每一个单独的函数。经过这种方式,就将运维操做转移到了 FaaS 平台,前端作服务端渲染,就不用再关心服务端程序的运维部署了。

ZEITNext.js 就对基于 Serverless 的服务端渲染作了很好的实现。下面就是一个简单的例子。

代码结构以下:

.
├── next.config.js
├── now.json
├── package.json
└── pages
    ├── about.js
    └── index.js
复制代码
// next.config.js
module.exports = {
  target: 'serverless'
}
复制代码

其中 pages/about.jspages/index.js 就是两个页面,在 next.config.js 配置了使用 Zeit 提供的 Serverless 服务。

而后使用 now 这个命令,就能够将代码以 Serverless 的方式部署。部署过程当中,pages/about.jspages/index.js 就分别转换为两个函数,负责渲染对应的页面。

基于 Serverless 的小程序开发

目前国内使用 Serverless 较多的场景可能就是小程开发了。具体的实现就是小程序云开发,支付宝小程序和微信小程序都提供了云开发功能。

在传统的小程序开发中,咱们须要前端工程师进行小程序端的开发;后端工程师进行服务端的开发。小程序的后端开发和其余的后端应用开发,本质是是同样的,须要关心应用的负载均衡、备份冗灾、监控报警等一些列部署运维操做。若是开发团队人不多,可能还须要前端工程师去实现服务端。

但基于云开发,就只须要让开发者关注于业务的实现,由一个前端工程师就可以完成整个应用的先后端开发。由于云开发将后端封装为了 BaaS 服务,并提供了对应的 SDK 给开发者,开发者能够像调用函数同样使用各类后端服务。应用的运维也转移到了提供云开发的服务商。

下面分别是使用支付宝云开发(Basement)的一些例子,函数就是定义在 FaaS 服务中的函数。

操做数据库

// `basement` 是一个全局变量
// 操做数据库
basement.db.collection('users')
	.insertOne({
		name: 'node',
		age: 18,
	})
	.then(() => {
		resolve({ success: true });
	})
	.catch(err => {
		reject({ success: false });
	});
复制代码

上传图片

// 上传图片
basement.file
	.uploadFile(options)
	.then((image) => {
		this.setData({
			iconUrl: image.fileUrl,
		});
	})
	.catch(console.error);
复制代码

调用函数

// 调用函数
basement.function
	.invoke('getUserInfo')
	.then((res) => { 
		this.setData({ 
			user: res.result
		});
	})
	.catch(console.error}
复制代码

通用 Serverless 架构

基于上述几个 Serverless 开发的例子,就能够总结出一个通用的 Serverless 架构。

其中最底层就是实现复杂业务的后端微服务(Backend)。而后 FaaS 层经过一系列函数实现业务逻辑,并为前端直接提供服务。对于前端开发者来讲,前端能够经过编写函数的方式来实现服务端的逻辑。对于后端开发者来讲,后端变得更靠后了。若是业务比较较淡,FaaS 层可以实现,甚至也不须要微服务这一层了。

同时无论是在后端、FaaS 仍是前端,咱们均可以去调用云计算平台提供的 BaaS 服务,大大下降开发难度、减小开发成本。小程序云开发,就是直接在前端调用 BaaS 服务的例子。

Serverless 开发最佳实践

基于 Serverless 开发模式和传统开发模式最大的不一样,就是传统开发中,咱们是基于应用的开发。开发完成后,咱们须要对应用进行单元测试和集成测试。而基于 Serverless,开发的是一个个函数,那么咱们应该如何对 Serverless 函数进行测试?Serverless 函数的测试和普通的单元测试又有什么区别?

还有一个很重要的点是,基于 Serverless 开发的应用性能如何?应该怎么去提升 Serverless 应用的性能?

本章主要就介绍一下,基于 Serverless 的函数的测试和函数的性能两个方面的最佳实践。

函数的测试

虽然使用 Serverless 咱们能够简单地进行业务的开发,但它的特性也给咱们的测试带来了一些挑战。主要有如下几个方面。

Serverless 函数是分布式的,咱们不知道也无需知道函数是部署或运行在哪台机器上,因此咱们须要对每一个函数进行单元测试。Serverless 应用是由一组函数组成的,函数内部可能依赖了一些别的后端服务(BaaS),因此咱们也须要对 Serverless 应用进行集成测试。

运行函数的 FaaS 和 BaaS 在本地也难以模拟。除此以外,不一样平台提供的 FaaS 环境可能不一致,不平台提供的 BaaS 服务的 SDK 或接口也可能不一致,这不只给咱们的测试带来了一些问题,也增长了应用迁移成本。

函数的执行是由事件驱动的,驱动函数执行的事件,在本地也难以模拟。

那么如何解决这些问题呢?

根据 Mike Cohn 提出的测试金字塔,单元测试的成本最低,效率最高;UI 测试(集成)测试的成本最高,效率最低,因此咱们要尽量多的进行单元测试,从而减小集成测试。这对 Serverless 的函数测试一样适用。


图片来源: martinfowler.com/bliki/TestP…

为了能更简单对函数进行单元测试,咱们须要作的就是将业务逻辑和函数依赖的 FaaS(如函数计算) 和 BaaS (如云数据库)分离。当 FaaS 和 BaaS 分离出去以后,咱们就能够像编写传统的单元测试同样,对函数的业务逻辑进行测试。而后再编写集成测试,验证函数和其余服务的集成是否正常工做。

一个糟糕的例子

下面是一个使用 Node.js 实现的函数的例子。该函数作的事情就是,首先将用户信息存储到数据库中,而后给用户发送邮件。

const db = require('db').connect();
const mailer = require('mailer');

module.exports.saveUser = (event, context, callback) => {
  const user = {
    email: event.email,
    created_at: Date.now()
  }

  db.saveUser(user, function (err) {
    if (err) {
      callback(err);
    } else {
      mailer.sendWelcomeEmail(event.email);
      callback();
    }
  });
};
复制代码

这个例子主要存在两个问题:

  1. 业务逻辑和 FaaS 耦合在一块儿。主要就是业务逻辑都在 saveUser 这个函数里,而 saveUser 参数的 eventconent 对象,是 FaaS 平台提供的。
  2. 业务逻辑和 BaaS 耦合在一块儿。具体来讲,就是函数内使用了 dbmailer 这两个后端服务,测试函数必须依赖于 dbmailer

编写可测试的函数

基于将业务逻辑和函数依赖的 FaaS 和 BaaS 分离的原则,对上面的代码进行重构。

class Users {
  constructor(db, mailer) {
    this.db = db;
    this.mailer = mailer;
  }

  save(email, callback) {
    const user = {
      email: email,
      created_at: Date.now()
    }

    this.db.saveUser(user, function (err) {
      if (err) {
        callback(err);
      } else {
        this.mailer.sendWelcomeEmail(email);
        callback();
      }
  });
  }
}

module.exports = Users;
复制代码
const db = require('db').connect();
const mailer = require('mailer');
const Users = require('users');

let users = new Users(db, mailer);

module.exports.saveUser = (event, context, callback) => {
  users.save(event.email, callback);
};
复制代码

在重构后的代码中,咱们将业务逻辑全都放在了 Users 这个类里面,Users 不依赖任何外部服务。测试的时候,咱们也能够不传入真实的 dbmailer,而是传入模拟的服务。

下面是一个模拟 mailer 的例子。

// 模拟 mailer
const mailer = {
  sendWelcomeEmail: (email) => {
	console.log(`Send email to ${email} success!`);
  },
};
复制代码

这样只要对 Users 进行充分的单元测试,就能确保业务代码如期运行。

而后再传入真实的 dbmailer,进行简单的集成测试,就能知道整个函数是否可以正常工做。

重构后的代码还有一个好处是方便函数的迁移。当咱们想要把函数从一个平台迁移到另外一个平台的时候,只须要根据不一样平台提供的参数,修改一下 Users 的调用方式就能够了,而不用再去修改业务逻辑。

小结

综上所述,对函数进行测试,就须要牢记金字塔原则,并遵循如下原则:

  1. 将业务逻辑和函数依赖的 FaaS 和 BaaS 分离
  2. 对业务逻辑进行充分的单元测试
  3. 将函数进行集成测试验证代码是否正常工做

函数的性能

使用 Serverless 进行开发,还有一个你们都关心的问题就是函数的性能怎么样。

对于传统的应用,咱们的程序启动起来以后,就常驻在内存中;而 Serverless 函数则不是这样。

当驱动函数执行的事件到来的时候,首先须要下载代码,而后启动一个容器,在容器里面再启动一个运行环境,最后才是执行代码。前几步统称为冷启动(Cold Start)。传统的应用没有冷启动的过程。

下面是函数生命周期的示意图:


图片来源: www.youtube.com/watch?v=oQF…

冷启动时间的长短,就是函数性能的关键因素。优化函数的性能,也就须要从函数生命周期的各个阶段去优化。

不一样编程语言对冷启动时间的影响

在此以前,已经有不少人测试过不一样编程语言对冷启动时间的影响,好比:


图片来源: Cold start / Warm start with AWS Lambda

从这些测试中可以获得一些统一的结论:

  • 增长函数的内存能够减小冷启动时间
  • C#、Java 等编程语言的能启动时间大约是 Node.js、Python 的 100 倍

基于上述结论,若是想要 Java 的冷启动时间达到 Node.js 那么小,能够为 Java 分配更大的内存。但更大的内存意味着更多的成本。

函数冷启动的时机

刚开始接触 Serverless 的开发者可能有一个误区,就是每次函数执行,都须要冷启动。其实并非这样。

当第一次请求(驱动函数执行的事件)来临,成功启动运行环境并执行函数以后,运行环境会保留一段时间,以便用于下一次函数执行。这样就能减小冷启动的次数,从而缩短函数运行时间。当请求达到一个运行环境的限制时,FaaS 平台会自动扩展下一个运行环境。

以 AWS Lambda 为例,在执行函数以后,Lambda 会保持执行上下文一段时间,预期用于另外一次 Lambda 函数调用。其效果是,服务在 Lambda 函数完成后冻结执行上下文,若是再次调用 Lambda 函数时 AWS Lambda 选择重用上下文,则解冻上下文供重用。

下面以两个小测试来讲明上述内容。

我使用阿里云的函数计算实现了一个 Serverless 函数,并经过 HTTP 事件来驱动。而后使用不一样并发数向函数发起 100 个请求。

首先是一个并发的状况:

能够看到第一个请求时间为 302ms,其余请求时间基本都在 50ms 左右。基本就能肯定,第一个请求对应的函数是冷启动,剩余 99 个请求,都是热启动,直接重复利用了第一个请求的运行环境。

接下来是并发数为 10 的状况:

能够发现,前 10 个请求,耗时基本在 200ms-300ms,其他请求耗时在 50ms 左右。因而能够得出结论,前 10 个并发请求都是冷启动,同时启动了 10 个运行环境;后面 90 个请求都是热启动。

这也就印证了以前的结论,函数不是每次都冷启动,而是会在必定时间内复用以前的运行环境。

执行上下文重用

上面的结论对咱们提升函数性能有什么帮助呢?固然是有的。既然运行环境可以保留,那就意味着咱们能对运行环境中的执行上下文进行重复利用。

来看一个例子:

const mysql = require('mysql');

module.exports.saveUser = (event, context, callback) => {

	// 初始化数据库链接
	const connection = mysql.createConnection({ /* ... */ });
	connection.connect();
  
	connection.query('...');

};
复制代码

上面例子实现的功能就是在 saveUser 函数中初始化一个数据库链接。这样的问题就是,每次函数执行的时候,都会从新初始化数据库链接,而链接数据库又是一个比较耗时的操做。显然这样对函数的性能是没有好处的。

既然在短期内,函数的执行上下文能够重复利用,那么咱们就能够将数据库链接放在函数以外:

const mysql = require('mysql');

// 初始化数据库链接
const connection = mysql.createConnection({ /* ... */ });
connection.connect();


module.exports.saveUser = (event, context, callback) => {

	connection.query('...');

};
复制代码

这样就只有第一次运行环境启动的时候,才会初始化数据库链接。后续请求来临、执行函数的时候,就能够直接利用执行上下文中的 connection,从而提后续高函数的性能。

大部分状况下,经过牺牲一个请求的性能,换取大部分请求的性能,是彻底能够够接受的。

给函数预热

既然函数的运行环境会保留一段时间,那么咱们也能够经过主动调用函数的方式,隔一段时间就冷启动一个运行环境,这样就能使得其余正常的请求都是热启动,从而避免冷启动时间对函数性能的影响。

这是目前比较有效的方式,但也须要有一些注意的地方:

  1. 不要过于频繁调用函数,至少频率要大于 5 分钟
  2. 直接调用函数,而不是经过网关等间接调用
  3. 建立专门处理这种预热调用的函数,而不是正常业务函数

这种方案只是目前行之有效且比较黑科技的方案,可使用,但若是你的业务容许“牺牲第一个请求的性能换取大部分性能”,那也彻底没必要使用该方案,

小结

整体而言,优化函数的性能就是优化冷启动时间。上述方案都是开发者方面的优化,固然还一方面主要是 FaaS 平台的性能优化。

总结一下上述方案,主要是如下几点:

  1. 选用 Node.js / Python 等冷启动时间短的编程语言
  2. 为函数分配合适的运行内存
  3. 执行上下文重用
  4. 为函数预热

总结

做为前端工程师,咱们一直在探讨前端的边界是什么。如今的前端开发早已不是以往的前端开发,前端不只能够作网页,还能够作小程序,作 APP,作桌面程序,甚至作服务端。而前端之因此在不断拓展本身的边界、不断探索更多的领域,则是但愿本身可以产生更大的价值。最好是用咱们熟悉的工具、熟悉的方式来创造价值。

而 Serverless 架构的诞生,则能够最大程度帮助前端工程师去实现本身的理想。使用 Serverless,咱们不须要再过多关注服务端的运维,不须要关心咱们不熟悉的领域,咱们只须要专一于业务的开发、专一于产品的实现。咱们须要关心的事情变少了,但咱们能作的事情更多了。

Serverless 也必将对前端的开发模式产生巨大的变革,前端工程师的职能也将再度回归到应用工程师的职能。

若是要用一句话来总结 Serverless,那就是 Less is More。

相关文章
相关标签/搜索