在本單元中,您將佈建 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,這是遊戲的唯一識別符。屬性 User1 和 User2 可存放正在玩遊戲的兩個使用者的使用者名稱。屬性 Heap1、Heap2 和 Heap3 可存放存儲三個堆積中每個堆積的物件數量。最後,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 訊息,以通知您的使用者其遊戲中的重要事件。