在本單元中,您將佈建 Amazon DynamoDB 資料庫並了解如何使用 DynamoDB 存放有關您的回合制遊戲的資訊。

完成單元的時間:30 分鐘


Amazon DynamoDB 是一種由 AWS 提供的全受管的 NoSQL 資料庫服務。它提供了低於 10 毫秒的回應時間和近乎無限的可擴展性。DynamoDB 可廣泛用於各種應用程式和產業,從 Amazon.com 購物車到 Lyft 的地理位置服務再到各種線上遊戲,不一而足。

與 DynamoDB 的所有互動均是透過使用 AWS Identity and Access Management (IAM) 進行身分驗證和授權的 HTTPS 完成的。通常情況下,您可使用適合您所選語言的 AWS 開發套件與 DynamoDB 互動。如果您正在使用適用於您應用程式的 AWS 運算選項,例如 Amazon Elastic Compute Cloud (Amazon EC2)AWS Lambda,則您的應用程式可以在運算環境中 AWS 登入資料向 DynamoDB 發出請求。

在接下來的步驟中,您應先佈建 DynamoDB 資料庫。然後,您將學習如何使用 適用於 Node.js 中 JavaScript 的 AWS 開發套件與 DynamoDB 資料庫互動。


  • 步驟 1.佈建 Amazon DynamoDB 資料庫

    首先,佈建 DynamoDB 資料庫。DynamoDB 中的資料庫又稱為資料表

    在建立 DynamoDB 資料表時,您需要指定要構成您的資料表的主索引鍵的屬性。您寫入 DynamoDB 資料表的每條記錄稱為項目,並且每個項目均必須包含您資料表的主索引鍵。

    DynamoDB 資料建模和主索引鍵設計是一個重要主題。但是,您的遊戲具有簡單的存取模式,不需要進階資料建模,因此本教學將不會介紹 DynamoDB 資料建模中的進階主題。如需在 DynamoDB 中進行資料建模的更多詳細資訊,您可以閱讀有關使用 Amazon DynamoDB 為行動應用程式設計資料庫使用 Amazon DynamoDB 對遊戲應用程式進行資料建模的教學。

    在您的應用程式中,您將 DynamoDB 用作簡單的鍵值存放。每個遊戲都有一個 gameId 屬性,且該屬性可唯一識別各個項目。gameId 屬性可用作您資料表的主索引鍵。

    scripts/ 目錄中,有一個名為 create-table.sh 的檔案,該檔案使用 AWS 命令列界面 (AWS CLI) 建立 DynamoDB 資料表。此檔案的內容如下:
     

    aws dynamodb create-table \
      --table-name turn-based-game \
      --attribute-definitions '[
        {
          "AttributeName": "gameId",
          "AttributeType": "S"
        }
      ]' \
      --key-schema '[
        {
          "AttributeName": "gameId",
          "KeyType": "HASH"
        }
      ]' \
      --provisioned-throughput '{
        "ReadCapacityUnits": 5,
        "WriteCapacityUnits": 5
      }'

    首先,其將資料表命名為 turn-based-game。然後,其會宣告將用於資料表的主索引鍵的屬性。在此範例中,您使用的是簡單的主索引鍵,因此您僅需宣告單一屬性 gameId,該屬性屬於字串資料類型。接下來,透過聲明將 gameId 屬性用作資料表的主索引鍵,即可來指定主索引鍵結構描述。

    最後,指定資料表所需的 讀取和寫入容量單位的數量。DynamoDB 確實具有隨需定價模式,且在該模式下,您需要為資料表中的每個讀取和寫入請求付費。但是,佈建的輸送模式適合 AWS 免費方案,因此您可以在此處予以使用。

    透過在終端機中執行以下命令來建立您的資料表:

    bash scripts/create-table.sh

    您應會在終端機中看到以下輸出:

    {
        "TableDescription": {
            "AttributeDefinitions": [
                {
                    "AttributeName": "gameId",
                    "AttributeType": "S"
                }
            ],
            "TableName": "turn-based-game",
            "KeySchema": [
                {
                    "AttributeName": "gameId",
                    "KeyType": "HASH"
                }
            ],
            "TableStatus": "CREATING",
            "CreationDateTime": 1574086642.07,
            "ProvisionedThroughput": {
                "NumberOfDecreasesToday": 0,
                "ReadCapacityUnits": 5,
                "WriteCapacityUnits": 5
            },
            "TableSizeBytes": 0,
            "ItemCount": 0,
            "TableArn": "arn:aws:dynamodb:us-east-1:955617200811:table/turn-based-game",
            "TableId": "c62cb86a-211e-4a50-a160-4a616c8f3445"
        }
    }
  • 步驟 2.將範例遊戲項目儲存在資料表中

    現在,您已經建立了資料表,您可以將項目新增至資料表中。每個遊戲均以資料表中的單一項目表示。

    每個遊戲項目的結構描述如下所示:

    每個遊戲均包含一個 GameId,這是遊戲的唯一識別符。屬性 User1User2 可存放正在玩遊戲的兩個使用者的使用者名稱。屬性 Heap1Heap2Heap3 可存放存儲三個堆積中每個堆積的物件數量。最後,LastMoveBy 屬性可指示最近移動的玩家。

    scripts/ 目錄中,有一個 createGame.js 檔案,其可將範例遊戲新增至您的資料表中。此檔案的內容如下:

    const AWS = require('aws-sdk')
    const documentClient = new AWS.DynamoDB.DocumentClient()
    
    const params = {
      TableName: 'turn-based-game',
      Item: {
        gameId: '5b5ee7d8',
        user1: 'myfirstuser',
        user2: 'theseconduser',
        heap1: 5,
        heap2: 4,
        heap3: 5,
        lastMoveBy: 'myfirstuser'
      }
    }
    
    documentClient.put(params).promise()
      .then(() => console.log('Game added successfully!'))
      .catch((error) => console.log('Error adding game', error))

    您匯入 AWS 開發套件,然後建立 DynamoDB 文件用戶端的執行個體。文件用戶端是低階 DynamoDB API 之上的更高層次的抽象,可簡化 DynamoDB 項目的使用。建立用戶端後,指令碼將為 PutItem API 呼叫組合參數,包括項目的資料表名稱和屬性。然後,其可在文件用戶端上呼叫 put() 方法並記錄成功或失敗的資訊。

    您可以透過在終端機中執行以下命令來執行指令碼,進而將遊戲插入資料表中:

    node scripts/createGame.js

    您應會在終端機中看到以下輸出:

    Game added successfully!

    注意:如果您執行命令的速度過快,則可能會收到錯誤訊息,表明該資料表尚不可用。等待一分鐘,然後重試該命令。

    太棒了! 現在,您已向您的資料表中新增了單一遊戲。在下一步中,您將學習如何更新遊戲項目以模擬使用者移動。

  • 步驟 3.更新資料表中的遊戲項目

    現在您的資料表中有一個遊戲項目,您可以學習如何模擬玩家移動進行中的遊戲。

    您可以透過兩種方式處理此操作。.第一種方法是,使用 GetItem API 擷取項目。然後,您根據玩家的移動來更新應用程式中的遊戲項目。最後,使用 PutItem API 取代 DynamoDB 中的現有項目。儘管此選項有效,但它需要對 DynamoDB 資料表申請多次請求,並有可能覆寫在擷取和重寫遊戲項目之間發生的任何變更。

    處理此操作的第二種方法是,在 DynamoDB 中使用 UpdateItem API 呼叫。使用 UpdateItem API,您可以透過單一請求就地更新 DynamoDB 項目。您指定要變更的項目、要變更的屬性以及要在該項目上主張的任何條件。這是變更項目的慣用方式,因為它不需要多次呼叫資料庫。

    scripts/ 目錄中,有一個名為 performMove.js 的檔案,其包含如下內容:

    const AWS = require('aws-sdk')
    const documentClient = new AWS.DynamoDB.DocumentClient()
    
    const performMove = async ({ gameId, user, changedHeap, changedHeapValue }) => {
      if (changedHeapValue < 0 ) {
        throw new Error('Cannot set heap value below 0')
      }
      const params = {
        TableName: 'turn-based-game',
        Key: { 
          gameId: gameId
        },
        UpdateExpression: `SET lastMoveBy = :user, ${changedHeap} = :changedHeapValue`,
        ConditionExpression: `(user1 = :user OR user2 = :user) AND lastMoveBy <> :user AND ${changedHeap} > :changedHeapValue`,
        ExpressionAttributeValues: {
          ':user': user,
          ':changedHeapValue': changedHeapValue,
        },
        ReturnValues: 'ALL_NEW'
      }
      try {
        const resp = await documentClient.update(params).promise()
        console.log('Updated game: ', resp.Attributes)
      } catch (error) {
        console.log('Error updating item: ', error.message)
      }
    }
    
    performMove({ gameId: '5b5ee7d8', user: 'theseconduser', changedHeap: 'heap1', changedHeapValue: 3 })

    該指令碼有點複雜,因此請逐步進行操作。

    與之前的指令碼一樣,您要匯入 AWS 開發套件並建立一個 DynamoDB 文件用戶端執行個體。

    然後,您可將方法定義為 performMove。此方法類似於當使用者請求移動時在應用程式中使用的內部方法。該指令碼為 UpdateItem API 呼叫組合參數。首先,它變更了遊戲的兩個屬性 - 要移動的最後一個使用者,以及在變更的堆積中的元素數量。

    然後,UpdateItem API 參數會對遊戲的目前狀態作出一些聲明。請先求取 ConditionExpression 的值,然後更新項目,以確認該項目處於所需狀態之前。您將在條件表達式中進行以下三種聲明:

    1. 請求執行移動的使用者是遊戲中兩個使用者中的一個;
    2. 目前回合屬於請求執行移動的使用者;
    3. 變更的堆積的目前值高於要變更的堆積的目前值。

    最後,參數聲明 ReturnValue ALL_NEW,這意味著 DynamoDB 在更新其值之後會傳回整個項目。有鑒於此,您的應用程式就可以評估遊戲,查看是否有獲勝者。

    該檔案的底部顯示了如何在應用程式中呼叫此方法的範例。使用以下命令執行指令碼:

    node scripts/performMove.js

    您應會在終端機中看到以下輸出:

    Updated game:  { heap2: 4,
      heap1: 3,
      heap3: 5,
      gameId: '5b5ee7d8',
      user2: 'theseconduser',
      user1: 'myfirstuser',
      lastMoveBy: 'theseconduser' }

    您可以看到寫入成功並且遊戲已更新。

    嘗試在終端機中再次執行指令碼。您應會看到以下輸出:

    Error updating item:  The conditional request failed

    此次,您的條件請求失敗了。正在請求的使用者 -- theseconduser -- 是最近移動的玩家。此外,變更的堆積 -- heap1 -- 已有數值 3,這表示使用者未進行任何變更。因此,該請求被拒絕。


在此單元中,您佈建了一個旨在存放您遊戲資料的 Amazon DynamoDB 資料庫。您在對資料建模時獲悉了 DynamoDB 中的主索引鍵。完成資料表建立,您已學會如何將項目插入資料表中以存放初始遊戲狀態。最後,您知曉如何更新您資料中的項目,以避免在單一請求中向 DynamoDB 發出多個請求。

在下一個單元中,您將學習如何使用 Amazon Simple Notification Service (Amazon SNS) 傳送 SMS 訊息,以通知您的使用者其遊戲中的重要事件。