設計實現SAM–無伺服器應用模型

Author:心譚
From:【Serverless】設計實現SAM–無伺服器應用模型
Des: 專註演算法與 web 開發的技術部落格

什麼是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定義的規則

參考鏈接