無伺服器 - 使用 DynamoDB 的 REST API



到目前為止,我們已經學習了幾個與無伺服器 Lambda 部署相關的概念。現在是時候看看一些示例了。在本章中,我們將研究 Serverless 官方提供的示例之一。我們將建立一個 REST API(正如其名稱所示)。您可能已經猜到,我們所有的 Lambda 函式都將由 API 閘道器觸發。我們的 Lambda 函式將與 DynamoDB 表(本質上是一個待辦事項列表)互動,使用者將能夠執行多種操作,例如建立新專案、獲取現有專案、刪除專案等,這些操作都將透過部署後公開的端點來完成。如果您不熟悉 REST API,可以點選這裡瞭解更多資訊。

程式碼演練

程式碼可在 GitHub 上找到 - https://github.com/serverless/examples/tree/master/aws-python-rest-api-with-dynamodb

我們將檢視專案結構,討論一些我們之前沒有見過的概念,然後執行 serverless.yml 檔案的演練。所有函式處理程式的演練都是多餘的。因此,我們將只演練一個函式處理程式。您可以將理解其他函式作為練習。

專案結構

現在,如果您檢視專案結構,Lambda 函式處理程式都位於 todos 資料夾中的單獨 .py 檔案中。serverless.yml 檔案指定了每個函式處理程式路徑中的 todos 資料夾。沒有外部依賴項,因此也沒有 requirements.txt 檔案。

新概念

現在,有一些術語您可能是第一次看到。讓我們快速瀏覽一下:

  • DynamoDB - 這是 AWS 提供的 NoSQL(不僅僅是 SQL)資料庫。雖然不是完全準確,但廣義地說,NoSQL 之於 SQL,就像 Word 之於 Excel。您可以點選這裡瞭解更多關於 NoSQL 的資訊。有四種類型的 NoSQL 資料庫:文件資料庫、鍵值資料庫、列式儲存和圖資料庫。DynamoDB 是一個鍵值資料庫,這意味著您可以將鍵值對不斷插入到資料庫中。這類似於 Redis 快取。您可以透過引用其鍵來檢索值。

  • Boto3 - 這是 Python 的 AWS SDK。如果您需要在 Lambda 函式中配置、管理、呼叫或建立任何 AWS 服務(EC2、DynamoDB、S3 等),則需要 Boto3 SDK。您可以點選這裡瞭解更多關於 Boto3 的資訊。

除此之外,在 serverless.yml 和處理程式函式的演練過程中,我們將遇到一些概念。我們將在那裡討論它們。

serverless.yml 演練

serverless.yml 檔案以服務的定義開始。

service: serverless-rest-api-with-dynamodb

接下來是透過以下行宣告框架版本範圍:

frameworkVersion: ">=1.1.0 <=2.1.1"

這就像一個檢查。如果您的無伺服器版本不在此範圍內,它將丟擲錯誤。當您共享程式碼並希望每個人都使用此 serverless.yml 檔案使用相同的無伺服器版本範圍以避免問題時,這很有幫助。

接下來,在 provider 中,我們看到兩個我們之前沒有遇到的額外欄位:environmentiamRoleStatements

provider:
   name: aws
   runtime: python3.8
   environment:
      DYNAMODB_TABLE: ${self:service}-${opt:stage, self:provider.stage}
   iamRoleStatements:
      - Effect: Allow
         Action:
         - dynamodb:Query
         - dynamodb:Scan
         - dynamodb:GetItem
         - dynamodb:PutItem
         - dynamodb:UpdateItem
         - dynamodb:DeleteItem
         Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:
         *:table/${self:provider.environment.DYNAMODB_TABLE}"

Environment,正如您可能猜到的那樣,用於定義環境變數。在此 serverless.yml 檔案中定義的所有函式都可以獲取這些環境變數。我們將在下面的函式處理程式演練中看到一個示例。在這裡,我們將 DynamoDB 表名稱定義為環境變數。

$ 符號表示變數。self 關鍵字指的是 serverless.yml 檔案本身,而 opt 指的是我們在 sls deploy 期間可以提供的選項。因此,表名稱將是服務名稱後跟連字元,然後是檔案找到的第一個階段引數:sls deploy 期間可用的一個選項,或者 provider 階段(預設為 dev)。因此,在這種情況下,如果您在 serverless deploy 期間不提供任何選項,DynamoDB 表名稱將為 serverless-rest-api-with-dynamodb-dev。您可以點選這裡瞭解更多關於無伺服器變數的資訊。

iamRoleStatements 定義授予函式的許可權。在這種情況下,我們允許函式對 DynamoDB 表執行以下操作:QueryScanGetItemPutItemUpdateItemDeleteItem。資源名稱指定允許執行這些操作的確切表。如果您在資源名稱的位置輸入了"*",則您將允許對所有表執行這些操作。但是,這裡我們只想允許對一個表執行這些操作,因此,此表的 ARN(Amazon 資源名稱)使用標準 ARN 格式提供在資源名稱中。同樣,這裡使用選項區域(在 serverless deploy 期間指定)或 provider 中提到的區域(預設為 us-east-1)中的第一個。

在 functions 部分,函式按照標準格式定義。請注意,get、update、delete 都有相同的路徑,id 作為路徑引數。但是,每種方法都不同。

functions:
   create:
      handler: todos/create.create
      events:
         - http:
            path: todos
            method: post
            cors: true
   list:
      handler: todos/list.list
      events:
         - http:
            path: todos
            method: get
            cors: true
   get:
      handler: todos/get.get
      events:
         - http:
            path: todos/{id}
            method: get
            cors: true

   update:
      handler: todos/update.update
      events:
         - http:
            path: todos/{id}
            method: put
            cors: true
   delete:
      handler: todos/delete.delete
      events:
         - http:
            path: todos/{id}
            method: delete
            cors: true

稍後,我們遇到了另一個我們之前沒有見過的塊,即resources 塊。此塊基本上可以幫助您指定您需要為函式工作而在 CloudFormation 模板中建立的資源。在這種情況下,我們需要建立一個 DynamoDB 表才能使函式工作。到目前為止,我們已經指定了表的名稱,甚至引用了它的 ARN。但是我們還沒有建立表。在resources 塊中指定表的特性將為我們建立該表。

resources:
   Resources:
      TodosDynamoDbTable:
         Type: 'AWS::DynamoDB::Table'
         DeletionPolicy: Retain
         Properties:
         AttributeDefinitions:
            -
               AttributeName: id
               AttributeType: S
         KeySchema:
            -
               AttributeName: id
               KeyType: HASH
         ProvisionedThroughput:
            ReadCapacityUnits: 1
            WriteCapacityUnits: 1
            TableName: ${self:provider.environment.DYNAMODB_TABLE}

這裡定義了很多配置,大多數配置都特定於 DynamoDB。簡而言之,我們要求無伺服器建立一個名為 'TodosDynamoDbTable' 的資源,其型別為 'DynamoDB Table',TableName(最底部提到)等於在 provider 的環境變數中定義的名稱。我們將它的刪除策略設定為 'Retain',這意味著如果堆疊被刪除,則保留資源。參見這裡。我們說表將有一個名為id的屬性,其型別為 String。我們還指定 id 屬性將是 HASH 鍵或分割槽鍵。您可以點選這裡瞭解更多關於 DynamoDB 表中 KeySchemas 的資訊。最後,我們指定表的讀取容量和寫入容量。

就是這樣!我們的 serverless.yml 檔案現在已準備好。現在,由於所有函式處理程式都大致相似,我們將只演練一個處理程式,即 create 函式的處理程式。

create 函式處理程式演練

我們從幾個匯入語句開始

import json
import logging
import os
import time
import uuid

接下來,我們匯入 boto3,如上所述,它是 Python 的 AWS SDK。我們需要 boto3 來在 Lambda 函式中與 DynamoDB 互動。

import boto3
dynamodb = boto3.resource('dynamodb')

接下來,在實際的函式處理程式中,我們首先檢查 'events' 負載的內容(create API 使用 post 方法)。如果它的主體不包含 'text' 鍵,則我們沒有收到要新增到待辦事項列表中的有效專案。因此,我們引發異常。

def create(event, context):
   data = json.loads(event['body'])
   if 'text' not in data:
      logging.error("Validation Failed")
      raise Exception("Couldn't create the todo item.")

考慮到我們按預期獲得了 'text' 鍵,我們為將其新增到 DynamoDB 表中做準備。我們獲取當前時間戳,並連線到 DynamoDB 表。注意如何在 serverless.yml 中獲取定義的環境變數(使用 os.environ)

timestamp = str(time.time())
table = dynamodb.Table(os.environ['DYNAMODB_TABLE'])

接下來,我們建立要新增到表中的專案,方法是使用 uuid 包生成隨機 uuid,使用接收到的資料作為文字,將 createdAt 和 updatedAt 設定為當前時間戳,並將欄位 'checked' 設定為 False。除了文字之外,您還可以使用 update 操作更新 'checked' 欄位。

item = {
   'id': str(uuid.uuid1()),
   'text': data['text'],
   'checked': False,
   'createdAt': timestamp,
   'updatedAt': timestamp,
}

最後,我們將專案新增到 DynamoDB 表中,並將建立的專案返回給使用者。

# write the todo to the database
table.put_item(Item=item)

# create a response
response = {
   "statusCode": 200,
   "body": json.dumps(item)
}
return response

透過此演練,我認為其他函式處理程式將不言自明。在某些函式中,您可能會看到此語句:"body" - json.dumps(result['Item'], cls=decimalencoder.DecimalEncoder)。這是一個用於json.dumps 中的錯誤的解決方法。json.dumps 預設情況下無法處理十進位制數,因此,建立了decimalencoder.py檔案來包含處理此問題的 DecimalEncoder 類。

恭喜您理解了使用無伺服器建立的第一個綜合專案。專案的建立者還在README檔案中分享了他的部署端點以及測試這些函式的方法。請檢視。進入下一章以檢視另一個示例。

廣告
© . All rights reserved.