【Serverless】设计实现SAM--无服务器应用模型

# 什么是 SAM?

sam 全称是:Serverless Application Model,也就是无服务器应用模型。

它使用 yaml 语法来描述一个应用程序,服务商会对.yml后缀的 sam 文件进行解析,并按照文件描述部署相关服务。

# 应用场景

SAM 的概念最初由 AWS 提出,用来描述程序所需要的 Lambda function、Cloud DB 等云端资源。

腾讯云云开发的扩展能力中,也使用 SAM 来描述扩展能力所需要的云开发资源,包括云函数、存储、数据库,甚至其他的云能力,例如短信发送。短信验证码登录 本身就是扩展,用到了腾讯云的短信能力。

再发挥一下,sam 可以用来描述简单的 UI 视图,尤其适合表单的应用场景,如下所示:

# 实现简易的 SAM

# 定义 SAM

因为 sam 是 yaml 语法文件,所以需要解析 yaml 语法,使用yaml.js

举个例子,某个程序需要使用到云函数,并且需要创建两个数据表,SAM 文件如下:

ApplicationName: 测试程序

# 云函数资源
Function:
  # 运行环境
  Container: nodejs 8.9
  # 超时时间(秒)
  Timeout: 60
  Corn:

# 云数据库资源
Database:
  # 需要创建的数据集合
  Collections:
    - CollectionName: "ext-collection-a"
    - CollectionName: "ext-collection-b"

# 数据校验

由于前端输入的数据不可信,后端需要对传入的 SAM 进行校验。

随着依赖的资源字段增加,单纯使用 if-else 的逻辑判断,会让代码变得难以维护,可读性非常差。

通常有 2 种数据校验的思路:

  • 借助 joi.js,在代码中增加校验逻辑
  • 使用 ajv.js,分离 Schema 和代码逻辑

第 2 种思路耦合度更低,并且规则的改动和维护,不涉及代码改动,产品和运营同学也可以来维护规则

按照 schema 的 ajv 语法,以前面的 SAM 文件为例,schema 的内容如下:

{
  "type": "object",
  "properties": {
    "ApplicationName": {
      "type": "string"
    },
    "Function": {
      "type": "object",
      "required": ["Container", "Timeout"],
      "properties": {
        "Container": {
          "type": "string"
        },
        "Timeout": {
          "type": "number"
        },
        "Corn": {
          "type": ["string", "null"]
        }
      }
    },
    "Database": {
      "type": "object",
      "Collections": {
        "type": "array",
        "items": {
          "properties": {
            "CollectionName": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}

封装 ajv 的验证逻辑:

const Ajv = require("ajv");
/**
 * 验证obj是否符合 Schema 定义
 * @param {object} obj
 * @param {string} schemaJson
 * @return {boolean}
 */
function validateSchema(obj, schemaFilePath) {
  const schemaJson = require(schemaFilePath);

  const ajv = new Ajv();
  const validate = ajv.compile(schemaJson);
  const valid = validate(obj);
  if (!valid) {
    console.log(">>> 错误字段信息:", validate.errors);
  }
  return valid;
}

# 变量注入

有些时候,某些变量是动态的。例如,用户信息可能在运行过程中被注入到上下文,数据集合名称需要前端用户表单传入。

举个例子,前面创建的两个数据集合的名称由前端表单传入,对应字段是:collectionNameAcollectionNameB

# 云数据库资源
Database:
  # 需要创建的数据集合
  Collections:
    - CollectionName: "${env.collectionNameA}"
    - CollectionName: "${env.collectionNameB}"

整个流程总结:

  • 服务端解析预设的 SAM 配置
  • 识别${}特殊字符串,替换变量
  • 验证是否符合 Schema 定义的规则

# 参考链接

Author:心谭
From:【Serverless】设计实现 SAM--无服务器应用模型
Des: 专注算法与 web 开发的技术博客