Nodejs也能Serverless?从0开始用Nodejs打造一个FaaS服务

2,031 阅读4分钟

本篇文章并不是教你怎么用阿里云、谷歌云等等云框架的 FaaS 服务,而是教你如何从 0 开始打造一个自己的 FaaS 服务。

受限于人力及成本原因,现在的 FaaS 服务基本上可以说是“大厂玩物”,而网上鲜有相关代码的实现,因此我才打算写一篇文章,和大家分享如何从 0 开始打造一个 Nodejs 的 FaaS 服务。

本篇文章中的项目 tiny-node-faas 已经上线并开源至 GitHub,欢迎各位使用。

源码地址:github.com/shadowings-…

一、背景及相关概念介绍

1-1、Serverless

在这里插入图片描述 Serverless,直译就是无服务器,是指构建和运行不需要服务器管理的应用程序的概念。Serverless 能够按需提供后端服务,用户可以直接编写和部署代码,而不必担心底层基础架构。

简单来说,如何判断一个服务是不是 Serverless 的呢?如果你在开发这个服务的时候,完全不知道“服务器多少”、“容器环境”等等和基础架构相关的东西的时候,那么这个服务就是 Serverless 的。

1-2、FaaS

FaaS 是 Function as a Service 的缩写,可以简单理解为功能服务化。FaaS 提供了一种服务碎片化的软件架构范式。FaaS 可以让研发只需要关注业务代码逻辑,不再关注技术架构。

举个例子吧,如果我们要使用 koa 框架写一个 hello world 服务,我们得这样写:

const Koa = require("koa");
const app = new Koa();

const handler = async (ctx) => {
  ctx.body = "hello world";
};

app.use(handler);
app.listen(8080, () => {
  console.log("8080端口已启动");
});

而如果使用 FaaS 服务,我们只需要关注下面这部分,其余的工作,比如服务创建、冷启动、负载均衡等等都由 FaaS 的提供方做了。

const handler = async (ctx) => {
  ctx.body = "hello world";
};

1-3、为什么要做 Nodejs 的 FaaS 服务

之所以做 Nodejs 的 FaaS 服务,是因为有越来越多的开发者,以及越来越多的后台服务,选择使用 Nodejs。

这其中当然有 Nodejs 本身“单线程异步非阻塞”的特性带给我们的简单直接的编程体验,也有一部分原因在于“前端全栈化”趋势会促使一部分前端工程师写后端代码逻辑。

而更值得关注的是,由于技术方向不同,不少前端开发工程师对运维、服务器的相关知识比较薄弱,因此为他们提供一个 Nodejs 的 FaaS 服务,能让其从运维中脱离出来,使开发者更聚焦于业务代码逻辑,是非常有前景的事情。

二、代码实现

2-1、vm 模块

“如何实现 Nodejs 的 FaaS 服务”,这个问题可以换成下面这个问题:

如何才能在 Nodejs 中新建一个 sandbox 并在这个 sandbox 中执行指定的代码,并拿到返回值呢?

Nodejs 实际上已经提供了这样一个模块——vm 模块,下面是文档: nodejs.org/api/vm.html

使用起来也很简单:

const vm = require("vm");

const code = 'console.log("hello world!")';

const sandbox = { console };
vm.createContext(sandbox);

const data = vm.runInNewContext(code, sandbox);

通过 vm 模块,我们就能在一个新的 sandbox 中执行 JavaScript 代码并拿到返回值了

2-2、整体架构

在这里插入图片描述 如图所示,tiny-node-faas 由四部分组成: 1、管理平台前端 2、函数管理功能 3、函数存储功能 4、函数执行功能

2、3、4 三者则是 FaaS 服务中最重要的三个组成部分。

2-3、核心代码

const vm = require("vm");

const runFunction = async (code) => {
  let timer = null;
  const result = await new Promise((resolve, reject) => {
    const sandbox = {
      require,
      console,
    };

    try {
      timer = setTimeout(() => {
        reject(new Error("Execute function time out"));
      }, 10000);

      vm.createContext(sandbox);

      const data = vm.runInNewContext(code, sandbox);

      resolve(data);
    } catch (error) {
      reject(error);
    }
  }).catch((err) => {
    return err instanceof Error ? err : new Error(err.stack);
  });

  if (timer) {
    clearTimeout(timer);
    timer = null;
  }

  return result;
};

上面代码中的runFunction方法就是最核心的执行 FaaS 函数的方法,我们只需要传入 code,即需要运行的代码,这个方法就会新建一个 sandbox 并运行代码,然后会将运行结果返回出来。

2-4、安全相关

当然,直接使用 vm 做 serverless 是有一定的安全隐患的。 比如使用require('os')会直接拿到物理机的操作系统权限,在这方面其实有很多有趣的涉及到诸如“沙箱逃逸”的话题。 GitHub 上也有“更加安全的 vm 模块的实现”,如vm2。 但受限于篇幅,以及本人的确不是什么攻防大神,这里就不再展开了。