AWSを使い倒す(15.Step Functions)
lambdaとAPI Gatewayの組み合わせが動いたと思った矢先、
思わぬところで問題発生。
API Gatewayってタイムアウト29秒までしか設定できないのね。
lambdaで普通に1分ぐらいかかる処理仕込んでたので
API Gatewayが待ちきれない状態になってしまった・・・。
【API Gatewayタイムアウト対策】Step Functionsを組み合わせて非同期処理にしてみる | Developers.IO
ということでこのStep Functionsを使って解決を試みる。
記事を読むにStep Functions(のステートマシン)を介してlambdaを実行することで、
lambda実行中なら実行中、終わったら結果をステートマシンが代わりに「応答」してくれるようになるので、API Gatewayのタイムアウトが回避できる。
代わりにAPI叩く人は結果返ってくるまで何回かAPIを叩かないといけないって感じ。
大変なのが
APIを叩いた時にユーザが渡してくるパラメータを
一度ステートマシンが受け取って
それをlambdaに渡す
ってしないといけないのでややこしい。
ここの理解にめちゃめちゃ時間がかかった。
まずは適当なlambda関数を作成する。
eventとしてStep Functionsから引数を受け取れるか、の確認するだけなので
本当にシンプルな作り。
次にStep Functionのステートマシンの作成
作成画面
コードスニペットで作成、タイプは標準を選ぶ
定義を書く。
ここにどう書くか、を理解するのが大変だった
今回書いたのはずばりこのコード
大事なのは、ResourceとParameters
Resourceには、lambda関数のarnを記載する。
Parametersは記法に戸惑うが、API Gatewayから変数として受け取るのでこう書く。
具体例を書くと、以下のようなフォーマットでAPI Gatewayから情報が来た時に
"input": {
"bucket: "nameofbucket",
"folder": "nameoffolder"
}
"s3bucket.$": "$.bucket"の意味は
s3bucketは変数です、変数の中身は来た情報の中のbucketが指し示す値です
ということになる
上記のような解釈で今度はステートマシンが
"event": {
"s3bucket": "nameofbucket",
"s3folder": "nameoffolder"
}
と情報を再構築してlambdaに渡してくれる。
これを元にlambdaは
event["s3bucket"]
として値を受け取ることができる。
$.だの.$だのをつけないと、変数ではなく定数と解釈されて受け取れなくなる。
この辺の何がどうなるはこちらで試されている
StepFunctionsの入力方法サンプルメモ集 - Qiita
作成すると、ARNが表示されるのでメモっておく。
API Gatewayの前にAPI Gateway用のIAMロールを作成しておく。
具体的には、StepFunctionsの実行権限を持ったロールが必要。
API Gateway を使用して Step Functions API を作成する - AWS Step Functions
ロールを作成したら、API Gatewayに移動
な形でメソッドとリソースを作成する。
executionがlambdaをキックするAPI、
statusがlambdaの進捗を確認するAPI。
メソッドの作成時に、StepFunctionsを選択する
重要なのはアクションのところ。
LambdaをキックするAPIにはStartExecution
進捗を確認するAPIにはDescribeExecution
と入力する。
実行ロールはさっき作ったIAMロールのarnを入力。
出来上がったら早速テストをする。
まずはlambdaの実行、つまりexecutionのPOSTから、テストの画面を開いた後
リクエスト本文に以下を入力
黒塗りのところにステートマシンのarnを記載する。
また、ステートマシンに渡したい変数を図のように記載する。
そしてテスト実行。
実行されたら
こんなレスポンスが返ってくる。
このexecutionArnを使って進捗確認を行うのでメモしておく。
次に進捗確認のほうのメソッド(/status)にうつる
リクエスト本文に、先ほどのexecutionArnを図のように記載する
これで実行すると、
先ほどキックしたlambdaが実行中であれば実行中、
実行完了していたら処理結果が返ってくる。
その進捗具合はステートマシンのログから確認ができる。
うまくいっているとこんな感じで
1.ExcecutionStartedにはAPIGatewayから送られてきた情報が
3でステートマシンがlamdaに渡した情報が
5でlambdaの実行結果が見える
一応この通りやれば動くハズ。
ここからは追加で
リクエスト本文にいちいちステートマシンのarn情報を記載しない方法と、
パスパラメータをステートマシン越しにlambdaへ渡す方法を記す。
まずは、実行したいステートマシンをあらかじめ記載しておく方法
この画面の統合リクエストのところをクリック
開いた画面の一番下、マッピングテンプレートを開く
ここにいろいろ書くことで、リクエスト本文の内容とかを書き換えることができる。
公式ドキュメントを参考に
API Gateway を使用して Step Functions API を作成する - AWS Step Functions
こんな感じで記載する。
stateMachineArnはもちろん自分のステートマシンに合わせて書き換える
右下の保存を押す。これでAPI実行の度にステートマシンの情報を書かなくてもよくなる。
次はパスパラメータ
パスパラメータを使うためのリソースとメソッドを作成する。
テストを押した時にここに
(めちゃ適当に)値を入れて、これがlambdaまで伝われば成功
こちらも統合リクエストでマッピングテンプレートの設定を行うことで実現できる
ずばり書くとこんな感じ。
参考にしたのは以下
API GatewayでStepFunctionsと統合する時のTips - Qiita
API Gateway マッピングテンプレートとアクセスのログ記録の変数リファレンス - Amazon API Gateway
これをテストすると、API Gatewayからの出力はこうなる
"input": {
"input": {},
"InputParams": {
"bucket": "a",
"folder": "b"
}
},
パスパラメータで引数を渡すし、実行時のステートマシン設定も不要になったので
テスト時にリクエスト本文に何も書く必要がなくなったため、
"input": {}、つまり記事の前半で参照していたinputには何も入らない。
代わりに、InputParams配下に自分の指定したパラメータが入っている。
ステートマシンにも変更が必要。
今まで受け取っていたinputの中身が空っぽになって、
代わりにInputParams配下に中身が入るので、そのような宣言がいる。
InputPathで宣言してやる。以下が参考
InputPath およびパラメータ - AWS Step Functions
lambdaも実態に合わせて変更("s3bucket"とかをただの"bucket"にしただけ)
これで、パスパラメータからステートマシンに、ステートマシンからlambdaに変数を渡していく仕組みが動く。
動きは確認できたので、あとは実装だ・・・!