今回はSQS+Lambdaを組み合わせて使う場合に気をつけるべきことについて書きました
サーバレスなアーキテクチャを考えた時に、何かと候補に上がる組み合わせだと思います
一部、自分が体験した失敗から学んだこともあるので、反省の復習をしつつ同じ失敗をする人が減ってくれれば良いなと思います
なんでSQS+Lambdaなのか?
そもそもこの組み合わせを選ぶのはなぜか?ということですが
- サーバレスなアーキテクチャを組みたい
- Lambdaを使用するにはトリガーとして全段にサービスが必要(SQS、APIGateway・・・など)
- Lambdaの処理が失敗した場合、再実行したい
このような理由からSQS+Lambdaの組み合わせを選択します
【ケース①】SQSの標準キューはメッセージの配信を1回”以上”保証する
SQSの標準キューとはこれです
「少なくとも1回の配信」とあります
複数回同じメッセージが配信されることもあります
そのため、Lambdaで2回以上同じメッセージが来ることを考慮する必要があります
/tmp
にファイル保存したり、DB処理するときに「重複してるからダメよ」と言われないように、「2回目以降は処理しない」など回避方法を考える必要があります
【ケース②】SQSの標準キューは順序性を保持しない
ケース①と同じく標準キューの話になります
FIFOキューであれば順序が保持されるのですが、標準キューは順序が保持されません
[1, 2, 3, 4]という順序でメッセージが入っても[1, 2, 4, 3]と処理されることがあります
順序性が必要な処理を行う際は「データをファイルやDBに全部まとめる前処理を置く」などデータを繋げたり、順序性が不要なデータにするなど対処が必要です
※「FIFOキューにすればいいじゃないか」という話はあるのですが、S3バケットのファイルを処理したい時に標準キューしか選べなかったりします
【ケース③】LambdaはSQSの複数メッセージを拾うことがある
LambdaはSQSのキューをポーリングして「処理するメッセージないかなー」と待っています
キューにメッセージが入ってくるとLambdaが処理を開始します
Lambdaが確認したタイミングでメッセージが複数存在すると複数のメッセージをLambdaの1起動分(プロセスと表現した方が良いのか?)で処理しようとすることがあります
import json
# "event"にメッセージが配列となって格納される
def lambda_handler(event, context):
Lambdaのタイムアウト、SQSキューの可視性タイムアウトの時間は「最大何メッセージを処理し、全体の処理時間は何秒か?」を考慮して設定する必要があります
また、途中のメッセージ(例えば10個中5個目)で例外などが発生した時に「次のメッセージを処理するのか?」など、ハンドリングを考慮する必要があります
1起動あたり何メッセージを取得するかはLambda側に設定します
Lambdaでトリガーを設定するところに「バッチサイズ」というのがあり、これが1バッチあたり何メッセージ取得するか?を決めています
Lambdaは1度に5バッチまで取得することが可能で、取得しきれないメッセージが存在するとLambdaの同時実行数が増えていきます
バッチはメッセージを入れる箱で、1度に箱に何メッセージ入れられて、箱を何個持てるか?持てない分は実行数を増やして箱を持つというイメージになります
AWS公式のドキュメントはこちらです
【ケース④】SQSのデッドレターキューを使用する際はLambdaは例外をスローする
SQSのキューの機能でデッドレターキューというものがあります
キューのメッセージを処理していると当然異常が発生すると思います
異常が発生したメッセージをキューに戻すと異常が繰り返し発生することになります
異常が発生したメッセージを保存しておく場所としてデッドレターキューを作ります
[キュー] → [Lambda(異常)] → [デッドレターキューにメッセージ入れる]
デッドレターキューにメッセージを入れるにはLambdaで例外をスローして処理を終了する必要があります
pythonを例にするとraise Exception
でfunctionの処理を終了すると自動的にメッセージがデッドレターキューに入ります
さいごに
最近、AWSがLambdaに力を入れていると思いますので、もっとサーバレスなアーキテクチャを選択することが増えてくることになると思っています
気軽に使えるのは間違いないのですが、Lambdaの仕様(クセ?)がもちろんあるので、理解しながら開発を進めて行くのが大事だと感じています
今後もLambdaに触れる機会はどんどん増えるので、そこで経験した知見やしくじりを書いていこうと思います