AWS LambdaをREST API Serverっぽく使ってみる

## はじめに
AWS SummitのLambda関連のSessionに触発されてLambdaを色々触っていますが、Lambdaを既存のREST  APIのServerの置き換えとして使えないか、という検討をしたメモをチラシの裏しておきます。

AWS Summitの資料は[こちらにあります。](http://aws.amazon.com/jp/summit2015-report/details/)Lambda関連のSession資料は以下2つ。

* [DEV-06 (Service）:Run Code in The Cloud : AWS Lambda 概要](http://media.amazonwebservices.com/jp/summit2015/docs/Dev-06s-Tokyo-Summit-2015.pdf)
* [DEV-08 (Mobile）:モバイル開発における AWS Lambda 活用法](http://media.amazonwebservices.com/jp/summit2015/docs/Dev-08m-Tokyo-Summit-2015.pdf
)

### 追記
Amazonが[API Gateway](http://aws.typepad.com/aws_japan/2015/07/amazon-api-gateway-build-and-run-scalable-application-backends.html)というServiceを発表しました。これを使えば、従来のREST API→API Gateway→Lambdaとつなげる事で、EC2無しでServer環境を作る事が可能になります。つまり、この記事に有る様なREST APIを無理矢理Amazon SDKへ変換して処理する必要は無くなります。

## Server/Clientの構成概要
#### Server側
現在、Server側のCodeであるLambdad functionは、Node.jsとjavaで書く事が可能です ([javaが先日サポートされました](http://aws.typepad.com/aws_japan/2015/06/aws-lambda-update-run-java-code-in-response-to-events.html))。ここではNode.jsを使い、Clientから送られて来た文字列を、全部大文字にして返す、と言う実装をCase studyとしています。

#### Client側
Buttonをclickすると、Text inputの中身を取り出してJSONにして、[AWS SDK for JavaScript in the Browser](http://aws.amazon.com/jp/sdk-for-browser/)の[Lambda関連のMethod](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html)をCallする、というhtmlを実装してみます。Responseの文字列とResponseに要した時間を表示してくれます。
  
AWS SDKを使うと処理が隠蔽されますが、CallしたMethodは、最終的には`https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/`へのPOST処理を行ってくれます。

## 事前準備
[こちらを参考に](http://qiita.com/studio-kakky/items/3791ba5f383f1797f73f)、以下をしておいてください。

* Lambda functionの作成
* Lambda functionを(AWSの内部で)動かす為のIAM roleの作成と権限の設定
* Lambda functionを(AWSの外部から)呼び出す為のIAM Userの作成と権限の設定、  
作成したIAM Userの`Access Key Id`、`Secret Access Key`の取得

上記Pageでは一部説明が省略されていますが、以下の2つのIAM関連の設定が必要です

* IAM role：  
起動されたLambda functionの為のAccess権の設定、今回はLambda functionからS3やDynamoへアクセスは無いので、Roleは空でOK
* IAM user：  
Lambda functionを外部から呼び出す為のUserで、このUserへのLambda functionの実行権限の付与と、このUserに対するAPI Key, Secret keyの作成が必要

## Server側のLambda functionのcode
たった4行ですが、Client側jsからPOSTされたJSONは、`context["clientContext"]`に入って居ます。このKeyValueの`["key1"]`という要素を取り出して、`toUpperCase`で大文字にして、Responseとして返します。

```js
exports.handler = function(event, context) {
    context.succeed(context["clientContext"]["key1"].toUpperCase());
    context.fail('Something went wrong');
};
```

## Client側のjavascriptのcode
以下をxx.htmlとして保存して実行してみてください。

個別に設定が必要なのが `accessKeyId`、`secretAccessKey`,`region`, `FunctionName`の4つです。

* `accessKeyId`、`secretAccessKey`は上記で作成した物を使ってください。以下のCodeの文字列は適当です
* `region`はここでは`US Virginia = us-east-1`としていますが、使っているAWS Regionの名前を入れてください
* `FunctionName`は作成したLambda functionの名前と同じ物にする必要が有ります

```js

<!DOCTYPE html>
<html>
<head lang="en">
    <script src="https://code.jquery.com/jquery-2.1.4.js"></script>
    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.1.34.min.js"></script>
    <script src="https://rawgit.com/dankogai/js-base64/master/base64.min.js"></script>
    <meta charset="UTF-8">
    <script type="text/javascript">
        $(document).ready(function(){
            $("#submit1").click(function(){
                executeLambda();
            });
        });
        executeLambda = function() {
            $("#submit1").attr("disabled", true);
            var startTime = new Date();
            AWS.config.update({accessKeyId: "AKIAJD66ABCDEFGHIJ", secretAccessKey:
                    "2CRDGTJZT9E42KC8GhxrcXXXrKyx7974634634fbsdfs" ,region:"us-east-1"});

            inputText = $("#input1").val();
            var input = {key1:inputText};
            var context = Base64.encode(JSON.stringify(input));
            var lambda = new AWS.Lambda();
            var params = {
                FunctionName:"API_Test",
                InvocationType:"RequestResponse",
                ClientContext:context
            };
            lambda.invoke( params,function(err,data) {
                var endTime = new Date();
                if(err) {
                    $("#lambdaResponse").append("<b>Error</b><br>");
                    console.log(err,err.stack);
                }
                else {
                    var responseTime = endTime - startTime;
                    $("#lambdaResponse").append("<b>Response = </b>" + data['Payload'] 
                                              + ", <b>Response time = </b>" + responseTime +  "msec<br>");
                }
                $("#submit1").attr("disabled", false);
            });
        };
    </script>
    <title>AWS Lambda invoke test</title>
</head>
<body>
    <input type="text" id="input1" style="width: 200px;">
    <button type="button" id="submit1">Send to Lambda</button>
    <div id="lambdaResponse"></div>
</body>
</html>
```

## 実際に使ってみて
Performance

* htmlでbuttonをclickしてresponseを得るまでで、早いと250 msec、遅いと8000 msecぐらい掛かります。比較用に同じくAWS US Virginiaに、ほぼ同様の処理をするnode.js serverを立ち上げてアクセスして見たところ、概ね100-400 msecでした
* Lambda function自体にCacheの機能があるとの事で、前回起動されてから間を空けずに再度起動された場合には処理が早くなると思います
* LambdaがTokyo regionにもサポートされたら、また測定してみたいと思います

今後の改善に期待した所

* Lambda function側の処理結果と標準出力のLogはCloudWatchのlogに吐き出されますが、Log UIが激しく使いにくいです