SQSのイベントをトリガーに起動するLambdaの仕組みを完全に理解する🧘♀️
先日S3のイベント駆動でLambdaをトリガーする記事を書きましたが、今回はSQSのイベントをトリガーにします。
LambdaのイベントソースにSQSが指定できるようになったのは意外にだいぶ遅めで、2018年4月です。FIFOキューはさらにその1年半後になります。
スタックの作成
AWSTemplateFormatVersion: "2010-09-09" Parameters: Name: Description: identifier Type: String Default: sqs-lambda-event Resources: Queue: Type: AWS::SQS::Queue Properties: QueueName: !Ref Name EventSourceMapping: Type: AWS::Lambda::EventSourceMapping Properties: Enabled: true EventSourceArn: !GetAtt Queue.Arn FunctionName: !GetAtt Lambda.Arn BatchSize: 1 Lambda: Type: AWS::Lambda::Function Properties: FunctionName: !Ref Name Handler: index.lambda_handler Role: !GetAtt LambdaRole.Arn Runtime: nodejs12.x Code: ZipFile: | exports.lambda_handler = async (event, context) => { const util = require('util'); console.log(util.inspect(event,false,null)); return 200; } LambdaRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess Policies: - PolicyName: sqs-access PolicyDocument: Statement: - Sid: 1 Effect: Allow Action: - sqs:DeleteMessage - sqs:GetQueueAttributes - sqs:ReceiveMessage Resource: - !GetAtt Queue.Arn
Lambdaの実行ロールにはSQSを扱うための以下の権限が必要です。
- sqs:DeleteMessage
- sqs:GetQueueAttributes
- sqs:ReceiveMessage
イベントの設定はLambda::EventSourceMapping
で行います。
バッチサイズにはメッセージの取得件数を指定します。
動作確認
バッチで10件のメッセージをキューに送信します。
aws sqs send-message-batch --queue-url $QUEUE_URL --entries file://entries.json
[ { "Id": "1", "MessageBody": "message: 1" }, { "Id": "2", "MessageBody": "message: 2" }, ...
BatchSize
が1なので、10回Lambdaが起動します。
ちなみに関数は同期呼び出しです。
event
の中身はこんな感じです。
{ "Records": [ { "messageId": "aa7e56f2-198b-46d2-9bdb-fd71ce34c02a", "receiptHandle": "AQEBJf14oNlhTcHzramlg4Zv800hpZSJu17DzO1L5B66TKlBN8Nq062rNWUljYtfB4nzKq8qxMwIZ1Bt5Zb99Ou5nHQixmo8WIf6oJTviKkqK4mKgmr+oC2YDR+FnsTHZclRXC04t6aeA/rHFizcNmF+zI/nqRj32URWkfQ8XoY+xipzAYFKQFdukfb1lOOA4f+hLdPqFm3Wq5LB0cM0E3xL3GhxEUU4jppW5thRMFQG2IVcU4Xt/UwIh5DuLvlNErTjCa2ROplPkNdjbtopHf075UH/ecg+CYyG1xgEOiwCaHAJXeco1zlLGP+dNHHDBWuPZktV8YR0jHRbD8LAfq5ku3TbSvJE8SnVz2tFZo5j9YVe5LdICWASXZl2qi1+sXjCjfmLeNyVzW/mJ70u4dXwNQ==", "body": "message: 1", "attributes": { "ApproximateReceiveCount": "1", "SentTimestamp": "1588712405385", "SenderId": "AIDAXTNEXWMNORT3ASWF5", "ApproximateFirstReceiveTimestamp": "1588712405388" }, "messageAttributes": {}, "md5OfBody": "6405aa584acecc53be5693fc1ecc83cb", "eventSource": "aws:sqs", "eventSourceARN": "arn:aws:sqs:ap-northeast-1:xxxxxxxx:sqs-lambda-event", "awsRegion": "ap-northeast-1" } ] }
バッチ処理に関して
例えばバッチサイズを10にした場合。
10個のインフライトメッセージが存在する場合でも、イベントが1つになるとは限りません。
動作確認ではメッセージが2つ、6つ、2つに分割されて3回起動されました。
これはイベントマッピングの問題ではなく、単純にSQSのアーキテクチャに依る仕様です。
裏側が分散システムになっているので、バッチサイズの最大数を取得できる保証はありません。
また、ドキュメントにはMaximumBatchingWindowInSeconds
というプロパティが存在しますが、SQSをイベントソースとする場合これは指定することができません。以下のエラーが発生します。
MaximumBatchingWindowInSeconds isn't supported for this event source type.
これはストリーム型のKinesisまたはDynamoDBのための設定値です。
実はロングポーリング
イベントとは実は名ばかりで、裏側ではロングポーリングが使用されています。上述のMaximumBatchingWindowInSeconds
が使えないのもそのためで、SQSのポーリング待機時間が優先されるためです。
具体的には基本5つのプロセスが並列で20秒のロングポーリングを行なっていて、インフライトメッセージの数に合わせてスケールするようになっています。最大で1000バッチ処理できるそうです。
気になるのが料金で、このポーリングの部分はしっかり課金されます。
全くメッセージがない場合でもLambdaのイベント設定が有効になっているだけで、およそ5×(60/20)×60×24×30=648,000/monthほどのリクエストが発生することになるので注意してください。
イベント駆動なので節約できると思いきや全然そんなことないので、ワーカーを自分で構築しなくて済むくらいの感じです。
メッセージの削除
関数が正常に終了すればメッセージは自動的に削除されます。
タイムアウト
Lambda関数のタイムアウトはメッセージの保持期間より短くする必要があります。処理中にタイムアウトするのを防ぐためです。
また、キューの可視性タイムアウトは関数のタイムアウト値の6倍以上に設定することが推奨されています。
FIFOキュー
S3のイベントでは通常キューしか使えませんでしたが、こちらはFIFOキューもサポートされています。