Amazon API GatewayにSNSをプロキシさせ複数処理を並列に行ってAWS IoTっぽくする
こんにちは、せーのです。今日はAPI Gatewayのちょっと面白い使い方をご紹介します。
APIリクエストを並列に処理したい
API Gatewayは簡単にAPIが作成できるとても便利なサービスですが、ソリューションとして考えると、リクエストに対して直列的に処理をして、レスポンスを返すという使い方が一般的です。例えばAPI Gatewayに来たAPIリクエストを元にLambdaを走らせて、LambdaからDynamoDBに値を登録して、200を返す、みたいな感じですね。
一方API Gatewayには「AWSサービスプロキシ」という統合タイプがあります。API Gatewayに来たリクエストを元にAPI Gatewayが直接AWSの他のリソースAPIを叩く、というものです。 それを使うと上の図のLambdaは要らなくなって、直接 API GatewayからDynamoDBに入れられたりします。
しかしクラウドをある程度触っている方は一つのデータを様々な方法に並列的に処理したい、と感じます。S3に生データを保存しつつDynamoDBにデータを入れて、かつSQSに投げて非同期に別処理、というような具合です。一つのLambdaの中にそれらの処理をゴリゴリ書いても良いのですが、ここはSNSを使って細かくコンポーネント的に処理する方法を考えてみましょう。
これが出来るとちょうどSNSがAWS IoTのRules Engineの代わりのようになるので、MQTTが投げられない、証明書が入れられないようなIoT機器をHTTPSのAPI経由で処理することも出来たりします。
やってみた
SNSのTopicを作成
それではやってみましょう。まずはSNSのトピックを作ります。ここはサクッとなにも考えずに作ってみます。 一応確認用に私のトピックにリクエストが飛んだらメールが飛ぶようにしておきます。
IAM Roleを作成
次にAPI GatewayからSNSにPublish出来るように権限を作っておきます。IAM Roleを任意の名前で作ります。
対象はAPI gatewayなのでAPI Gatewayを選びます。
CloudWatchにログが残るようにポリシーをつけておきます。
できたIAM RoleにSNSのポリシーを追加します。
キチンと作るのであればここでPublishのみ、特定トピックのみに権限を絞る必要があります。テストということでフルつけちゃいます。
これでできあがり。
API Gatewayを作成
いよいよAPI Gatewayを作ります。
名前は自由につけましょう。
APIは名詞で設定するのが定石、ということでリソースにID的なものが入れられるように変数にしてみたりします。
そんな感じでリソースを重ねました。メソッドはPOSTを指定します。統合タイプに[AWSサービスプロキシ]を選択し、POSTに対してPublishアクションを上書きします。Roleは先程作ったIAM RoleのARNを指定します。
AWS Proxy部分を設定
最後にSNSとAPI Gatewayをつなげます。作ったメソッドから[統合リクエスト]を開きます。
ここからが今回の工夫ポイントとなります。SNSのAPIを叩くログを見てみるとクエリに全てのパラメータが乗っかっています。ですのでAPIに来たリクエストをプロキシ先のSNSのクエリパラメータとしてマッピングします。 設定する項目はTopicArn, Subject, Messageの3つです。TopicArnにはSNSのTopicのARNを、Subjectにはパスに指定してあるIDを、Messageにはリクエストとしてきた値をそのまま入れます。
TopicArnはシングルクォーテーションで囲むことでAPI Gatewayが固定値として認識してくれます。
これでプロキシの設定は完了です。
テスト
それではテストしてみましょう。テストのリンクをクリックします。
パスの変数[deid]にID(っぽいの)を入れて流したいデータを書き込み、テストボタンを押します。
結果が隣のペインに表示されます。無事通ったようです。
APIを見てみると、設定したパラメータもキチンと通っているようです。
テスト用に設定したメールを見てみると、こちらにも届いています。
このSNSにLambdaを新たにつけてみましょう。中身は単純にログを吐く、というだけにしています。
このLambdaをSNSに追加します。
もう一度テストしてみます。テストボタンをおしたらCloudWatch Logsから作ったLambdaのログを見てみると、リクエスト内容がLambdaに通っているのがわかります。あとはこの要領で好きなサービスにデータを流すLambda Functionを書いてはSNSにつけていけば並列処理の完成です。
まとめ
いかがでしたでしょうか。こんな感じでAPIから並列処理を簡単に行うことが出来ました。クエリパラメータにしないでリクエストBodyのままで飛ばす方法はないか(Content-Typeをtext/plainにしたらイケる、という情報がありましたがやってみたらダメでした。知ってる人いたら教えてください)とか、まだ改善の余地はあるものの、アーキテクチャとしてはとてもAWSっぽいと思います。皆さんも是非試してみてください。