kazu22002の技術覚書

PHPer, Golang, AWS エンジニアの日々

AWS SNS + Lambdaでpushイベントの仕組みでweb apiを実行

push型のイベントで処理をしたいと思ってAWS SNS + Lambdaで作成してみました。

aws.amazon.com

今回のコードはphppythonでやっています。

AWS SNSのトピック追加

f:id:kazu22002:20210726090821p:plain

f:id:kazu22002:20210726090831p:plain

Lambdaを使いたいためタイプをスタンダードにします。

f:id:kazu22002:20210726090840p:plain

f:id:kazu22002:20210726090855p:plain

トピックを作成できたら、入力ソースに指定する部分は完成です。

f:id:kazu22002:20210726091239p:plain

phpでの入力

composerでaws/aws-sdk-phpをインストール

composer install aws/aws-sdk-php

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通信

pythonjsonの値を渡すpost通信

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周りのでバックがうまくできないので今度調べてみたいですね。