AWS SNS + Lambdaでpushイベントの仕組みでweb apiを実行
push型のイベントで処理をしたいと思ってAWS SNS + Lambdaで作成してみました。
AWS SNSのトピック追加
Lambdaを使いたいためタイプをスタンダードにします。
トピックを作成できたら、入力ソースに指定する部分は完成です。
phpでの入力
composerでaws/aws-sdk-phpをインストール
composer install aws/aws-sdk-php
use Aws\Sns\SnsClient; use Aws\Exception\AwsException; use Aws\Credentials\Credentials; class PartTimeEvent { private $aws_key = "AWS_KEY_ID"; private $aws_secret = "AWS_SECRET_KEY"; private $credentials; private $sns_user_apn = "arn:aws:sns:ap-northeast-1:******:****"; public function __construct( ){ $this->credentials = new Credentials($this->aws_key, $this->aws_secret); } private function sns($data){ $client = new SnsClient([ // 'profile' => 'default', 'region' => 'ap-northeast-1', 'version' => 'latest', 'credentials' => $this->credentials, ]); $topic = $this->sns_user_apn; try { $result = $client->publish([ 'Message' => json_encode($data), 'TopicArn' => $topic, ]); } catch(AwsException $e){ var_dump($e->getMessage()); } } }
データはjsonで渡します。
Lambda
SNSから受け取る値のサンプル。Lambdaのテストテンプレートをそのまま掲載。
{ "Records": [ { "EventSource": "aws:sns", "EventVersion": "1.0", "EventSubscriptionArn": "arn:aws:sns:ap-northeast-1:{{{accountId}}}:ExampleTopic", "Sns": { "Type": "Notification", "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", "TopicArn": "arn:aws:sns:ap-northeast-1:123456789012:ExampleTopic", "Subject": "example subject", "Message": "example message", "Timestamp": "1970-01-01T00:00:00.000Z", "SignatureVersion": "1", "Signature": "EXAMPLE", "SigningCertUrl": "EXAMPLE", "UnsubscribeUrl": "EXAMPLE", "MessageAttributes": { "Test": { "Type": "String", "Value": "TestString" }, "TestBinary": { "Type": "Binary", "Value": "TestBinary" } } } } ] }
PHPからMessageに入力を登録するため、Lambda側ではMessageの値を取得します。今回はpythonで取得し処理を実行します。
import json def lambda_handler(event, context): for record in event['Records']: print(json.loads(record['Sns']['Message']))
pythonでHttp通信
import json import urllib.request import os url = "http://example.com/***" headers = { 'Content-Type': 'application/json', } def postApi(data): req = urllib.request.Request(url, json.dumps(data).encode(), headers) with urllib.request.urlopen(req) as res: body = res.read() print(body)
動作はPHPで実行し、ログはCloudWatchを確認。とりあえず動作するところまでやってみました。
最初はSQSを使用
AWS SQS + Lambdaを利用して、SQSにキューが挿入されたことをキーにLambdaを起動させると良さそう。と思って構築しましたが、Lambdaの入力トリガーをSQSにした場合、ポーリングしているためLambdaの処理時間が細かく追加され無料枠をほとんど消費してしまう結果になりました。
ポーリングとは、一定時間の間隔で処理を実行する仕組みです。
キューに登録することで、登録する側の待機時間を減らそうと思っていましたが、Lambdaの無料枠を消費しつくしたため断念しました。
まとめ
アプリケーション間の連携をなにかサーバーレスでやりたいと思って作りましたが、いまだに正解がわからないです。
相変わらずいろんなことをして失敗を繰り返している気がします。
サーバーレスで色々とやってみたいところから触っています。簡単になっていけばいいですが、まだLambda周りのでバックがうまくできないので今度調べてみたいですね。