Help us understand the problem. What is going on with this article?

【AWS】API GatewayからLambdaを非同期で実行する

More than 1 year has passed since last update.

diagram.png

はじめに

最近Web APIを素早く用意するためにAWSのAPI Gateway + Lambdaの構成をよく使います。
しかし、サービスの質の向上や、仕様のためAPIはレスポンスを早く返さなければならない場合が多々あります。
ネットで検索すると、API GatewayからInvocationTypeを指定してLambdaを非同期呼び出しする事で先にレスポンスを返してしまう話はよく出てきますが、Lambda側で必要な処理が書かれていない事が多く、大抵のテストコードではプロセスをキルされてしまい、Lambdaが処理を停止します。(今回はNode.jsの記事なので、Pythonなどでは必要ないのかも?)
なので設定例を自分用備忘録的に書き残しておきます。

画像が多くて見辛いかもしれません。

処理の流れ

  1. API Gatewayがリクエストを受け取る
  2. 非同期でLambdaを呼び出す
  3. LambdaからPromiseを返す
  4. API Gatewayがステータスコード:202のレスポンスを返す

今回の構成

利用サービス

  • API Gateway
  • Lambda
    • Node.js(8.10)

構成図

diagram.png

実際の設定

処理を行うLambdaの作成

テストコードでは少し重い処理を想定し、2秒後に処理を終了するようにしました。

index.js
exports.handler = async (event, context, callback) => {
    let promise = new Promise (function (resolve, reject) {
        // 処理開始時にstartと時刻を出力
        console.log("start: " + (new Date()));
        // 2秒待ってから次の処理へ
        setTimeout(function(){
            resolve();
        }, 2000);
    })
    promise
    .then(function(){
        // 処理終了時にendと時刻を出力
        console.log("end: " + (new Date()));
        callback(null, {
            statusCode: 200,
            body: JSON.stringify('OK'),
        });
    });
    // Lambdaの呼び出し元へPromiseを返す
    return promise;
};

ここが重要なのですが、呼び出されたLambdaからPromiseを返さないと呼び出し元(本記事ではAPI Gateway)はレスポンスを待つ事になります。
試しにreturn promise;の部分をコメントアウトなどしてみてください。呼び出し元は処理を待たされます。

API Gatewayの作成

今回はLambdaから新規APIを作成します。
add_trigger.png

作成したらAPI Gatewayのコンソールで設定していきます。

Lambdaからそのまま作るとLambdaプロキシ統合が設定されています。
統合リクエストをクリックして、リクエストを設定していきます。
integration_request.jpg

proxy_check.jpg

「Lambdaプロキシ統合の使用」のチェックを外します。
これで普通のLambda統合になり、HTTPヘッダーを入力できるようになります。

HTTPヘッダーを追加し
名前: X-Amz-Invocation-Type
マッピング: 'Event'
を設定します。

http_header.png

※ マッピングはJSONPathで書く事になりますが、今回は常に非同期にしたいのでシングルクォーテーションで囲った固定値を書き込みます。

APIをデプロイしてアクセスしてみます。

Chrome拡張機能のRestlet Client、簡単にテストできるので重宝してます。

api_test.jpg

リクエストを送ると2秒も待たずに134ミリ秒でレスポンスが返ってきました。

cloudwatch_log01.jpg

ログを見るとLambdaはプロセスを維持していて2秒後にendを出力している事がわかります。

次は試しにLambdaのコードでreturn promise;をコメントアウトして実行したログ。
cloudwatch_log02.jpg

endを出力する事なく23ミリ秒程度でプロセスが終了している事がわかります。

ひとまずこれで非同期処理は完成です。

レスポンスの加工

このままだと常に200を返してしまうので、レスポンスのデフォルトHTTPステータスを202にします。
※ 今回は202以外使わない想定です。

メソッドレスポンス → 統合レスポンスの順で設定をします。
response_before.png

メソッドレスポンス

デフォルトで入っている200を削除して202を追加します。
method_before.png
 ▼
method_after.png

統合レスポンス

こちらも同じく200を削除して202を追加します。
追加の際、正規表現には-(ハイフン)を入れます。
integration_before.png
 ▼
integration_after.png

このようになります。
response_after.png

APIを再度デプロイしてリクエストしてみます。
api_test2.jpg
HTTPステータス:202が無事に返ってきました。

おわりに

Lambdaを使って非同期処理をする場合、例えば最初のLambdaから他のLambdaなどの別の処理を呼び出す方が汎用性がありますが、並列処理をしない場合や、取り回しが複雑でない場合には直接非同期呼び出しをする方が実行回数も抑えられて良いかなと思います。

foxwell
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away