AWS Lambdaを使ってバナーの出し分けをしてみる


AWS Lambdaが正式サービスとしてリリースされました！（2015年4月9日JST）
そのタイミングで、Synchronous Eventsという機能が追加されました。
これは、実行した結果を同期型でレスポンスを受け取ることができます。
プレビュー時点では、InvokeAsyncで非同期だったので処理結果をクライアント側で受け取るためには一工夫必要でした。同期で受け取ることができるようになったので、これまで以上に、様々なシーンで利用できそうです。

今回は、ベーシックな動的Webで利用できないかと考え、例えば、ユーザによってバナーを差し替えるロジックをLambdaとS3のみで実現してみました。
EC2無しで動的なページを作ることが目的ですね。

##イメージ
![1.png](https://qiita-image-store.s3.amazonaws.com/0/60724/53b7db0f-b8cb-2dd0-f6a1-40700a70a698.png "1.png")

今回は、Cognito IDの下一桁が奇数と偶数で出すバナーを変えます。

##Amazon DynamoDBの準備
DynamoDBには単純にIDとURLだけのテーブルを作ります。
IDは、奇数なら0、偶数なら1を出しようなイメージです。

| ID (HashKey)| URL     |
|:------------|---------|
|0            |Banner-A |
|1            |Banner-B |

##AWS Lambdaの準備

既に様々のところでLambdaの設定方法などの情報サイトがあるので詳細は割愛します。
発行されたCognito Identiyを元に適切なURLをDynamoDBからURLを取得します。

```Javascript
var aws = require('aws-sdk');
var dynamodb = new aws.DynamoDB();

exports.handler = function(event, context) {
    var num = parseInt(event.key.substr(event.key.length - 1, 1),16);
    console.log(num);
    var flag = '0';
    if (num % 2 === 0) flag = '1';  
    var params = {
		TableName: 'LambdaWebSiteDemo',
			Key: {
				'id': {
					N: flag
				}
			}
		};
		dynamodb.getItem(params,function(err, data) {
			if (!err) {
				console.log("Data: " + data['Item']['URL']['S']);
				context.succeed(data['Item']['URL']['S']);
			}else{
				console.log("Error: " + err);
			}
        });
};

```
context.succeed()で、レスポンスを返すことができるようになりました。
今回は、DynamoDBに格納されているバナーURLが返却されます。

IAMロールの設定がかなり楽になりました。
ウィザード上で選べるようになってます。
今回は、DynamoDBにアクセスするので、DynamoDBへの読み取り権限を付与しておきます。

##クライアント側（Javascript）
クライアント側は、CognitoでCredentialsを取得し、LambdaにInvokeします。

```Javascript
<!DOCTYPE html>
<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Lambda Demo</title>
  <script src="https://sdk.amazonaws.com/js/aws-sdk-2.1.23.min.js"></script>
<script type="text/javascript">
	/*
	/ Cognito: get credential
	*/
	var client_id;
	var params = {
		AccountId: "アカウントID",
		RoleArn: "ロールARN",
		IdentityPoolId: "Cognito Identity Pool ID",
	};
	AWS.config.region = 'us-east-1';
	AWS.config.credentials = new AWS.CognitoIdentityCredentials(params);
	AWS.config.credentials.get(function(err) {
		if (!err) {
			client_id = AWS.config.credentials.identityId;
			console.log("Cognito Identity Id: " + client_id);
			document.getElementById("cognito_id").innerHTML = client_id;
			getImage(client_id);
		} else {
			console.log("Error:" + err);
		}
	});
	/*
	/ Lambda インスタンス化
	*/
	var lambda = new AWS.Lambda();
</script>
</head>
<body>
    <div class="container header">
		<h1>バナーのだしわけをしてみる</h1>
    </div>
	<div class="container">
		Cognito ID: <p id="cognito_id"></p>
	</div>
    <div class="container">
			<img id="Image">
    </div>

<script type="text/javascript">
	/* 
	/ Lambda InvokeでCognitoIDをキーでイメージ取得
	*/
	function getImage(id){
		console.log("getImage Func: " + id);
		var data = {
			'key': id
		};
		var params = {
			FunctionName: 'LambdaWebSite',
			InvocationType: 'RequestResponse',
			LogType: 'Tail',
			Payload: JSON.stringify(data)
		};
		lambda.invoke(params, function(err, data) {
			if (err){
				 console.log(err, err.stack);
			}else{
				var url = data.Payload.replace(/\"/g,"");
				console.log(url);
				document.getElementById("Image").src = url;
			}
		});
	};

</script>
</body>
</html>
```
lambda.invoke()でLambda Functionを実行できます。
InvocationTypeによって、同期(RequestResponse)か非同期(Event)かを選択できます。
また、LogTypeは、TailかNoneが選択できます。Tailを選択すると、最新の4KBのログがBase64エンコードされた状態で、x-amz-log-resultsに格納されます。
こんな感じ。（ChromeのLive Http headで取得）
![Kobito.5eNQNx.png](https://qiita-image-store.s3.amazonaws.com/0/60724/93f09201-81a2-e5b1-2dbf-1e9f49074d2f.png "Kobito.5eNQNx.png")

Lambda Functionからのレスポンスは、以下の様なフォーマットで返って来ます。
 {StatusCode: 200, Payload: "レスポンス値"}

詳細は[こちら](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html)をご覧ください


##最後に
同期型で呼び出しができるようになったのは本当に便利ですね。
まだまだ、可能性が広がりそうです。


# 免責
こちらは個人の意見で、所属する企業や団体は関係ありません。