Amazon API GatewayにSNSをプロキシさせ複数処理を並列に行ってAWS IoTっぽくする

API Gateway

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、せーのです。今日はAPI Gatewayのちょっと面白い使い方をご紹介します。

APIリクエストを並列に処理したい

API Gatewayは簡単にAPIが作成できるとても便利なサービスですが、ソリューションとして考えると、リクエストに対して直列的に処理をして、レスポンスを返すという使い方が一般的です。例えばAPI Gatewayに来たAPIリクエストを元にLambdaを走らせて、LambdaからDynamoDBに値を登録して、200を返す、みたいな感じですね。

api-gw-proxy1

一方API Gatewayには「AWSサービスプロキシ」という統合タイプがあります。API Gatewayに来たリクエストを元にAPI Gatewayが直接AWSの他のリソースAPIを叩く、というものです。
それを使うと上の図のLambdaは要らなくなって、直接 API GatewayからDynamoDBに入れられたりします。

api-gw-proxy2

しかしクラウドをある程度触っている方は一つのデータを様々な方法に並列的に処理したい、と感じます。S3に生データを保存しつつDynamoDBにデータを入れて、かつSQSに投げて非同期に別処理、というような具合です。一つのLambdaの中にそれらの処理をゴリゴリ書いても良いのですが、ここはSNSを使って細かくコンポーネント的に処理する方法を考えてみましょう。

api-gw-proxy3

これが出来るとちょうどSNSがAWS IoTのRules Engineの代わりのようになるので、MQTTが投げられない、証明書が入れられないようなIoT機器をHTTPSのAPI経由で処理することも出来たりします。

やってみた

SNSのTopicを作成

それではやってみましょう。まずはSNSのトピックを作ります。ここはサクッとなにも考えずに作ってみます。
一応確認用に私のトピックにリクエストが飛んだらメールが飛ぶようにしておきます。

api-gw-proxy17

IAM Roleを作成

次にAPI GatewayからSNSにPublish出来るように権限を作っておきます。IAM Roleを任意の名前で作ります。

api-gw-proxy5

対象はAPI gatewayなのでAPI Gatewayを選びます。

api-gw-proxy6

CloudWatchにログが残るようにポリシーをつけておきます。

api-gw-proxy7

できたIAM RoleにSNSのポリシーを追加します。

api-gw-proxy8

キチンと作るのであればここでPublishのみ、特定トピックのみに権限を絞る必要があります。テストということでフルつけちゃいます。

api-gw-proxy9

これでできあがり。

api-gw-proxy10

API Gatewayを作成

いよいよAPI Gatewayを作ります。

api-gw-proxy11

名前は自由につけましょう。

api-gw-proxy12

APIは名詞で設定するのが定石、ということでリソースにID的なものが入れられるように変数にしてみたりします。

api-gw-proxy13

そんな感じでリソースを重ねました。メソッドはPOSTを指定します。統合タイプに[AWSサービスプロキシ]を選択し、POSTに対してPublishアクションを上書きします。Roleは先程作ったIAM RoleのARNを指定します。

api-gw-proxy14

AWS Proxy部分を設定

最後にSNSとAPI Gatewayをつなげます。作ったメソッドから[統合リクエスト]を開きます。

api-gw-proxy15

ここからが今回の工夫ポイントとなります。SNSのAPIを叩くログを見てみるとクエリに全てのパラメータが乗っかっています。ですのでAPIに来たリクエストをプロキシ先のSNSのクエリパラメータとしてマッピングします。
設定する項目はTopicArn, Subject, Messageの3つです。TopicArnにはSNSのTopicのARNを、Subjectにはパスに指定してあるIDを、Messageにはリクエストとしてきた値をそのまま入れます。

TopicArnはシングルクォーテーションで囲むことでAPI Gatewayが固定値として認識してくれます。

api-gw-proxy20

これでプロキシの設定は完了です。

テスト

それではテストしてみましょう。テストのリンクをクリックします。

api-gw-proxy18

パスの変数[deid]にID(っぽいの)を入れて流したいデータを書き込み、テストボタンを押します。

api-gw-proxy19

結果が隣のペインに表示されます。無事通ったようです。

api-gw-proxy21

APIを見てみると、設定したパラメータもキチンと通っているようです。

api-gw-proxy22

テスト用に設定したメールを見てみると、こちらにも届いています。

api-gw-proxy23_1

このSNSにLambdaを新たにつけてみましょう。中身は単純にログを吐く、というだけにしています。

api-gw-proxy24

このLambdaをSNSに追加します。

api-gw-proxy25

もう一度テストしてみます。テストボタンをおしたらCloudWatch Logsから作ったLambdaのログを見てみると、リクエスト内容がLambdaに通っているのがわかります。あとはこの要領で好きなサービスにデータを流すLambda Functionを書いてはSNSにつけていけば並列処理の完成です。

api-gw-proxy26_1

まとめ

いかがでしたでしょうか。こんな感じでAPIから並列処理を簡単に行うことが出来ました。クエリパラメータにしないでリクエストBodyのままで飛ばす方法はないか(Content-Typeをtext/plainにしたらイケる、という情報がありましたがやってみたらダメでした。知ってる人いたら教えてください)とか、まだ改善の余地はあるものの、アーキテクチャとしてはとてもAWSっぽいと思います。皆さんも是非試してみてください。