في هذه الوحدة، ستقوم بتوفير قاعدة بيانات ‏Amazon DynamoDB‏ وستتعلم كيفية استخدام DynamoDB لتخزين معلومات حول لعبتك القائمة على الأدوار.

الوقت اللازم لاستكمال الوحدة: 30 دقيقة


Amazon DynamoDB هي قاعدة بيانات NoSQL مُدارة بالكامل مقدمة من AWS. وهي توفر أوقات استجابة بالمللي ثانية من رقم واحد وقابلية للتطوير تقترب من اللانهائية. تُستخدم DynamoDB بواسطة مجموعة واسعة من التطبيقات والصناعات، بدءًا من عربة تسوق Amazon.com إلى خدمة تحديد الموقع الجغرافي في Lyft إلى مجموعة واسعة من الألعاب عبر الإنترنت.

تتم جميع أشكال التفاعل مع DynamoDB عبر HTTPS باستخدام AWS Identity and Access Management (IAM) للمصادقة والترخيص. عادةً ما تستخدم AWS SDK للغة التي تختارها للتفاعل مع DynamoDB. فإذا كنت تستخدم خيارات حوسبة AWS للتطبيق الخاص بك، مثل Amazon Elastic Compute Cloud (Amazon EC2‏) أو AWS Lambda، فيمكن للتطبيق الخاص بك استخدام بيانات اعتماد AWS في بيئة الحوسبة خاصتك لتقديم طلبات إلى DynamoDB.

في الخطوات التالية، ستقوم أولًا بتوفير قاعدة بيانات DynamoDB. بعد ذلك، سوف تتعلم كيفية التفاعل مع قاعدة بيانات DynamoDB باستخدام AWS SDK for JavaScript in Node.js.


  • الخطوة 1. تقديم قاعدة بيانات Amazon DynamoDB

    أولًا، لنقم بتقديم قاعدة بيانات DynamoDB. يُطلق كذلك على قاعدة البيانات في DynamoDB اسم جدول.

    عند إنشاء جدول DynamoDB، سوف تحتاج إلى تحديد السمة (السمات) التي ستشكل المفتاح الرئيسي لجدولك. يُسمى كل سجل تكتبه في جدول DynamoDB‏ عنصرًا، ويجب أن يتضمن كل عنصر المفتاح الرئيسي لجدولك.

    تُعد نمذجة بيانات DynamoDB وتصميم المفتاح الرئيسي موضوعًا مهمًا. ومع ذلك، تتمتع لعبتك بنمط وصول بسيط لا يتطلب نمذجة بيانات متقدمة، لذلك لن يغطي هذا البرنامج التعليمي الموضوعات المتقدمة في نمذجة بيانات DynamoDB. وللحصول على تفاصيل إضافية حول نمذجة البيانات في DynamoDB، يمكنك قراءة البرامج التعليمية حول تصميم قاعدة بيانات لتطبيق جوال باستخدام Amazon DynamoDB‏ أو ‏نمذجة بيانات تطبيق الألعاب باستخدام Amazon DynamoDB‏.

    في تطبيقك، يمكنك استخدام DynamoDB كمخزن بسيط بقيمة أساسية. تحتوي كل لعبة على سمة gameId تعرّف كل لعبة بشكل فريد. تُستخدم سمة gameId كمفتاح رئيسي لجدولك.

    في دليل scripts/، يوجد ملف يسمى create-table.sh ينشئ جدول DynamoDB الخاص بك باستخدام واجهة سطر أوامر AWS ‏(AWS CLI). وتكون محتويات الملف كالتالي:
     

    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 SDK ثم إنشاء مثيل من DynamoDB Document Client‏. إن Document Client هو عبارة عن استخلاص عالي المستوى على واجهة برمجة تطبيقات DynamoDB ذات المستوى المنخفض ويسهل العمل مع عناصر DynamoDB. بعد إنشاء العميل، يقوم البرنامج النصي بتجميع المعلمات لاستدعاء واجهة برمجة تطبيقات PutItem، بما في ذلك اسم الجدول والسمات في العنصر. ثم يستدعي طريقة put () على Document Client ويقوم بتسجيل معلومات حول النجاح أو الفشل.

    يمكنك تشغيل البرنامج النصي لإدراج لعبة في جدولك بتشغيل الأمر التالي في المحطة الطرفية:

    node scripts/createGame.js

    وستظهر النتيجة التالية في الوحدة الطرفية:

    Game added successfully!

    ملاحظة: إذا قمت بتشغيل الأمر بسرعة كبيرة، فقد تظهر رسالة خطأ بأن الجدول غير متوفر بعد. انتظر دقيقة واحدة، ثم حاول الأمر مرة أخرى.

    عظيم! لقد أضفت الآن لعبة فردية إلى جدولك. في الخطوة التالية، سوف تتعلم كيفية تحديث عنصر اللعبة هذا لمحاكاة مستخدم يقوم بحركة.

  • الخطوة 3. تحديث عنصر لعبة في جدولك

    الآن بعد أن أصبح لديك عنصر لعبة في جدولك، يمكنك معرفة كيفية محاكاة لاعب يقوم بحركة في لعبة قيد التقدم.

    هناك طريقتان يمكنك من خلالهما التعامل مع هذه العملية. في الطريقة الأولى، يمكنك استرداد العنصر باستخدام واجهة برمجة تطبيقات GetItem. بعد ذلك، ستقوم بتحديث عنصر اللعبة في تطبيقك وفقًا للحركة التي قام بها اللاعب. وأخيرًا، يمكنك استبدال العنصر الموجود في DynamoDB باستخدام واجهة برمجة تطبيقات PutItem. رغم فاعلية هذا الخيار، إلا أنه يتطلب طلبات متعددة لجدول DynamoDB ويخاطر باستبدال أي تغييرات حدثت بين جلب عنصر اللعبة وإعادة كتابته.

    الطريقة الثانية لمعالجة هذه العملية هي استخدام استدعاء واجهة برمجة التطبيقات UpdateItem في DynamoDB. باستخدام واجهة برمجة تطبيقات UpdateItem، يمكنك تحديث عنصر 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 SDK وإنشاء مثيل DynamoDB Document Client.

    وبعد ذلك، يمكنك تحديد طريقة تسمى performMove. تتشابه هذه الطريقة مع طريقة داخلية سيتم استخدامها في التطبيق الخاص بك عندما يطلب أحد المستخدمين القيام بحركة. يقوم البرنامج النصي بتجميع المعلمات لاستدعاء واجهة برمجة تطبيقات UpdateItem. أولًا، يغير سمتين في اللعبة -- آخر مستخدم يقوم بحركة، وعدد العناصر في الكومة التي تم تغييرها.

    بعد ذلك، تقوم معلمات واجهة برمجة تطبيقات UpdateItem بتقديم بعض التأكيدات حول الحالة الحالية للعبة. يتم تقييم 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) لإرسال رسائل نصية قصيرة لإعلام المستخدمين بالأحداث المهمة في لعبتهم.