More than 1 year has passed since last update.

概要

Lambdaでジャンケンゲームを作ります。
http://lambda-janken.s3-website-ap-northeast-1.amazonaws.com [停止中]

Screen Shot 2014-11-26 at 5.52.41 PM.png
※データが流れている部分を枠で表示しています
 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
Screen Shot 2014-11-26 at 5.52.49 PM.png
※グーチョキパーで結果を表示します
※素材:ごっこあそび - http://gokkoasobi.com/omen_jyanken.html

AWSのサービスをフル活用します。
「JavaScriptだけで出来るやん・・」というツッコミは置いておいて、以下のサービスを組み合わせて実装します。
Amazon Cognito
Amazon Kinesis
Amazon Lambda
Amazon SQS
Amazon S3

ブラウザで受けたリクエストをKinesis に流し込み、これを受けて発火したLambdaファンクションで、グー・チョキ・パーを表す乱数を生成し、SQSにメッセージとしてに結果をキューイングします。ブラウザはキューイングされたSQSを取得し、グー・チョキ・パーのイラストを表示して切り替えます。

※現時点でLambdaはプレビューのため、すべて us-east-1 リージョンを利用します。

Kinesis 設定

AWSマネージメントコンソールから適当な名前でストリームを作成します。

setting
stream_name = LambdaJanken
ARN = arn:aws:kinesis:us-east-1:xxxxxxxxxx:stream/LambdaJanken
number of shard = 1

SQS 設定

AWSマネージメントコンソールから適当な名前でキューを作成します。

setting
queue_name = LambdaJanken
URL = https://sqs.us-east-1.amazonaws.com/xxxxxxxxxx/LambdaJanken"
ARN = arn:aws:sqs:us-east-1: xxxxxxxxxx:LambdaJanken

Lambda 設定

index.jsを作成します。

index.js
var aws = require('aws-sdk');
var kinesis = new aws.Kinesis({region:'us-east-1'});
var sqs = new aws.SQS({region:'us-east-1'});
exports.handler = function(event, context) {
   var rand = Math.floor( Math.random() * 3 );
   var params = {
      MessageBody: String(rand),
      QueueUrl: 'https://sqs.us-east-1.amazonaws.com/xxxxxxxxxx/LambdaJanken',
      DelaySeconds: 0,
   };
   sqs.sendMessage(params, function(err, data) {
     if (err) {
        console.log(err, err.stack);
     }else{
       console.log(data);
       context.done();
     }
   });
}

index.jsをzip化して、index.zipを作成します。

AWS CLIからFunctionを作成、アップロードします。

upload-function
aws lambda upload-function \
--region us-east-1 \
--function-name LambdaJanken \
--function-zip index.zip \
--runtime nodejs \
--role arn:aws:iam::xxxxxxxxxx:role/lambda_exec_role \
--handler index.handler \
--mode event \
--timeout 30

AWS CLIからイベントをアタッチします。

add-event-source
aws lambda add-event-source \
--region us-east-1 \
--function-name LambdaJanken \
--role arn:aws:iam::xxxxxxxxxx:stream:role/lambda_invoke_role \
--event-source arn:aws:kinesis:us-east-1:xxxxxxxxxx:stream/LambdaJanken \
--batch-size 1 \
--parameters InitialPositionInStream=LATEST

Cognito 設定

AWSマネージメントコンソールから適当な名前でプールを作成します。

setting
identity_pool_name = LambdaJanken
identity_pool_id = us-east-1:cf2xxxx-1xxx-4xxx-9xxx-1856xxxxxxxx
Unauthenticated Identities is checked

IAM 設定

Cognito_LambdaJankenUnauth_DefaultRole
{
    "Version": "2012-10-17",
    "Statement": [{
        "Action": [
            "mobileanalytics:PutEvents",
            "cognito-sync:*"
        ],
        "Effect": "Allow",
        "Resource": [
            "*"
        ]
    }]
}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "kinesis:*",
      "Resource": "arn:aws:kinesis:us-east-1:xxxxxxxxxx:stream/LambdaJanken"
    }
  ]
}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "sqs:*"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:sqs:us-east-1:xxxxxxxxxx:LambdaJanken"
    }
  ]
}
Entities-cognito-identity.amazonaws.com
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Federated": "cognito-identity.amazonaws.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "cognito-identity.amazonaws.com:aud": "us-east-1:cxxxxx-1xxx-4xxx-9xxx-1xxxxxxxx"
        },
        "ForAnyValue:StringLike": {
          "cognito-identity.amazonaws.com:amr": "unauthenticated"
        }
      }
    }
  ]
}

HTML 作成

index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lambda ジャンケン</title>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.0.28.min.js"></script>
<style type="text/css"><!--
    .waku{  position: absolute; top: 180px; left: 95px; z-index: 1; }
    .gu{    position: absolute; top: 20px; left: 180px; z-index: 1; display: none;  }
    .choki{ position: absolute; top: 20px; left: 180px; z-index: 1; display: none;  }
    .pa{    position: absolute; top: 20px; left: 180px; z-index: 1; display: none;  }
    textarea {  margin-left: 50px; width: 850px; height: 300px; line-height: 1.5em; }
--></style>
</head>
<body>
<img src="LambdaJanken.png" />
<img src="LambdaJanken_waku.png" class="waku" id="waku" />
<img src="gu.png" class="gu" id="gu" />
<img src="choki.png" class="choki" id="choki" />
<img src="pa.png" class="pa" id="pa" />
<textarea cols="40" rows="7" id="tex"></textarea>
<p style="padding-left:50px">素材提供:ごっこあそび - http://gokkoasobi.com/omen_jyanken.html</p>

<script>
var janken;
var tex = document.getElementById('tex');
var waku = document.getElementById('waku');

//Cognito
var params = {
    AccountId: "xxxxxxx",
    RoleArn: "arn:aws:iam::xxxxxxx:role/Cognito_LambdaJankenUnauth_DefaultRole",
    IdentityPoolId: "us-east-1:cxxxxxx-1xxx-4xxx-9xxx-1xxxxxxx"
};
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials(params);
AWS.config.credentials.get(function(err) {
    if (!err) {
        //Log
        tex.value = tex.value + "===== Cognito authentication =====" + "\n"
        tex.value = tex.value + "Cognito Identity Id: " + AWS.config.credentials.identityId + "\n";     
        //CSS
        waku.style.left = "280px";
    }
});

//Kinesis
var kinesis = new AWS.Kinesis();
var params1 = {
          Data: 'Hello', /* required */
          PartitionKey: 'partitionkey', /* required */
          StreamName: 'LambdaJanken', /* required */
};
kinesis.putRecord(params1, function(err, data) {
    if (err){
        console.log(err, err.stack); // an error occurred
    }else{
        //Log
        tex.value = tex.value + "===== Kinesis put object =====" + "\n";
        tex.value = tex.value + "SequenceNumber: " + data.SequenceNumber + "\n";
        tex.value = tex.value + "ShardId: " + data.ShardId + "\n";  
        //Lambda
        tex.value = tex.value + "===== Lambda invoke function =====" + "\n";

        //CSS
        waku.style.left = "465px";

        //Log
        tex.value = tex.value + "===== SQS send message =====" + "\n";      

        //SQS
        var sqs = new AWS.SQS();
        var params2 = {
            QueueUrl: 'https://sqs.us-east-1.amazonaws.com/xxxxxxxx/LambdaJanken', /* required */
            MaxNumberOfMessages: 1,
            WaitTimeSeconds: 5
        };
        sqs.receiveMessage(params2, function(err, data) {
            if (err) {
                console.log(err, err.stack); // an error occurred
            }else{              
                janken = data.Messages[0].Body;

                //CSS
                waku.style.left = "650px";

                //Log
                tex.value = tex.value + "===== SQS retrieve message =====" + "\n";
                tex.value = tex.value + "ReceiptHandle: " + data.Messages[0].ReceiptHandle + "\n";
                tex.value = tex.value + "Body: " + data.Messages[0].Body + "\n";

                var params3 = {
                              QueueUrl: 'https://sqs.us-east-1.amazonaws.com/xxxxxxxxxxx/LambdaJanken', /* required */
                              ReceiptHandle: data.Messages[0].ReceiptHandle /* required */
                };
                sqs.deleteMessage(params3, function(err, data) {
                    if (err){
                        console.log(err, err.stack); // an error occurred
                    }else{
                        //Log
                        tex.value = tex.value + "===== SQS delete message =====" + "\n";
                        tex.value = tex.value + "RequestId: " + data.ResponseMetadata.RequestId + "\n";

                        //CSS
                        waku.style.display = "none";

                        //Display Janken
                        switch (Number(janken)){
                            case 0:
                                var ele = document.getElementById('gu');
                                ele.style.display = "block";
                                break;
                            case 1:
                                var ele = document.getElementById('choki');
                                ele.style.display = "block";
                                break;
                            case 2:
                                var ele = document.getElementById('pa');
                                ele.style.display = "block";
                                break;
                        }
                    }
                });
            }
        });
    }
});
</script>
</body>
</html>
ソースツリー
|-index.html(本体)
|-LambdaJanken.png (背景)
|-LambdaJanken_waku.png (オレンジ枠)
|-gu.png(グー)
|-choki.png(チョキ)
|-pa.png(パー)

これらファイルをS3へアップロードして公開します。

免責
こちらは個人の意見で会社とは関係ありません。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.