[黒魔術] AWS IoTを使って外から自宅に疑似HTTPリクエストを送ってみた

AWS運用自動化サービス「Cloud Automator」

こんにちは、プロセスエンジニアリング部 プロセスエンジニアリング1課の中村です。
最近の悩みは、展示会やイベントの申込フォームの所属部署名欄が文字数制限で弾かれることです。

突然ですが、皆さんはこんなことを考えたことはありませんか?

自宅のWebサーバーのAPIを外から叩きたい…

でも、固定IPを割り振っているわけでもなく、DDNSも面倒くさい…VPNも嫌だ…
とはいえ、ngrokやserveoはセキュリティが怖い…
MQTT使えばグローバルIPアドレスを持ってない端末と通信できるけど、そもそもプロトコルが違うし、PublishとSubscribeはそれぞれ独立してるからPublishのResponseを同期で受け取れない…

そこで私はひらめきました。

AWS IoTをAPI Gatewayでラップして、MQTTのTopicを予めRequestとResponseのそれぞれで決めておけば、疑似的なHTTPリクエストっぽいことができるんじゃないか…!?

こんな具合です。怖いですね。LambdaからSubscribeなんてできるんでしょうか。

できました。というわけでサンプルのソースコードです。
mqttjsをラップして、リクエストクライアントぽくしてみました。

https://github.com/galactic1969/mqtt-async-requests

使い方は、Clientのインスタンスを生成して、doするだけです。doする際の引数で、TopicにUUIDを足すかどうか(Topicの衝突防止)、QoS、データが届くまでの待ち時間などを設定できるようにしています。

使い方は以下のサンプルを参考にしてみてください。Serverless FrameworkでAPI Gateway、Lambdaがデプロイされるようになっています。なお、このサンプルでは、HTTPのHeaderやMethodなどは考慮せず、単純にAPI GatewayからのGETリクエストを受け取り次第AWS IoTに決め打ちのテキストを送っています。認証も実装していませんので、ご利用の際にはご注意下さい。

https://github.com/galactic1969/mqtt-async-requests

動かしてみる

実際に動かしてみましょう。上記のサンプルを動かして時間を測定してみます。(今回は上の構成図の番号の、6, 7 を省略し、自宅のMQTT ClientでReceiveしたら即Publishするようにしています)

左側画面がMQTT Clientのログ、右側がcURLコマンドでHTTPリクエストを行っている様子です。

なんということでしょう…。MQTTブローカーとの接続だけでも1秒くらいかかるイメージだったのですが、HTTPリクエスト全体で0.2sec~0.4sec程度で返ってきました。流石にLambdaのコールドスタート時には1秒程度かかったのですが、それでも期待より早い数値でした。素晴らしいですね!あとはCognito認証なりCustom Authorizerを実装してあげれば、認証付きのAPIの出来上がりです!

とはいえ、今回の構成は多少無理がある構成です。ブローカーにAWS IoTを利用する場合は、次の制限値を気にしておく必要があります。

  • ブローカーへの同時接続数: 500,000
  • 1秒あたりのMQTT CONNECTリクエスト(アカウント単位): 500
  • 1秒あたりのMQTT CONNECTリクエスト(ClientId単位): 1
  • 1秒あたりのSUBSCRIBEリクエスト数(アカウント単位): 500
  • アクティブなサブスクリプション数: 500,000

上記の中でも、1秒あたりのMQTT CONNETリクエスト数、SUBSCRIBEリクエスト数が重要です。今回の構成では、API Gatewayにリクエストが来るたびにCONNECT, SUBSCRIBEリクエストが飛ぶので、実質秒間500リクエストが上限のAPIになるといっても良いでしょう。それもアカウント単位の制限なので複数のAPIを実装する場合はより少なくなります。自宅のAPIをちょろっと呼ぶくらいに留めておいたほうが良いでしょう。ご利用は計画的に。

ちなみに、コネクションを残したままLambdaを終了できるかどうかも試してみましたが、偶数回の起動毎に例外が発生してしまい、できなさそうでした。残念。

AWS運用自動化サービス「Cloud Automator」