【これで理解できる】SQSのデッドレターキューを理解したかったのでSQS+Lambdaのリトライ処理を実装してみた


本記事は以下ブログの内容を転記したものです。
以下ブログの方が見やすいと思います。気になる方はどうぞです。

https://memomaru.life/lambda-sqs-dlq-retry/

## はじめに
最近、仕事でSQSを使う機会があったのですが、デッドレターキューやSQSによるリトライ処理がイマイチ理解できていませんでした。
AWSの公式ドキュメントやブログ記事などを見てもあまり理解できなかったため、実際にサンプルアプリを作って検証しました。

## 実施内容(概要)

実施内容は以下です。

- LambdaにSQSでメッセージを送信する
- Lambdaはエラーとなるような処理とする
- 複数リトライ後、SQSのメッセージがデッドレターキューに入ることを確認する
- デッドレターキューに入ったメッセージを使って再処理できることを確認する

## 実施内容(詳細 - 設定)

### IAM

一般的なユースケースからLambdaを選択し、`次のステップ：アクセス権限`を押します。

ポリシーとして`AmazonSQSFullAccess`と`CloudWatchLogsFullAccess`を選択します。理由はそれぞれ以下です。選択後、`次のステップ：タグ`を押します。

- `AmazonSQSFullAccess`:LambdaがSQSメッセージを処理するため
- `CloudWatchLogsFullAccess`: LambdaがSQSメッセージ処理後にCouldWatch logsにログを出力するため

タグの設定はスキップし、`次のステップ：確認`を押します。

一通りの内容を確認します。また、ロール名は適当に付けて`ロールの作成`を押します。

これでIAMの設定は完了です。

### Lambda

次にLambdaで検証用の関数を作成します。

サンプルアプリの言語としてPythonを選択します。

アクセス権限は`既存のロール`を選択し、先ほど作成したロールを選択します。

コードは以下のコードを貼り付けて、必ずエラーとなるようにします。

```
import json

def lambda_handler(event, context):
    msg = event['Records'][0]['body']
    print(msg)
    try:
        a = 1 / 0 
    except:
        raise Exception('ERROR!!')
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
```

以下でSQSの設定後に、`トリガーを追加`をクリックし、以下設定します。

- トリガーとしてSQSを選択
- SQSキュー：sample-message-queue
- バッチサイズ：1
- バッチウィンドウ：0

### SQS

今回は以下2つのキューを作成します。

- sample-message-queue:上記Lambdaにメッセージを送信するキュー
- sample-message-queue-dlq：デッドレターキューを格納するキュー

それぞれ以下のように作成していきます。

**デッドレターキューを格納するキュー**

- タイプ：標準
- 名前：sample-message-queue-dlq
- 設定：デフォルト
- アクセスポリシー：デフォルト

**Lambdaにメッセージを送信するキュー**

- タイプ：標準
- 名前：sample-message-queue
- 設定：デフォルト
- アクセスポリシー：デフォルト
- デッドレターキュー：有効
- キューの選択：sample-message-queue-dlq
- 最大受信数：5:sample-message-queueが5回リトライ後、メッセージがsample-message-queue-dlq移動する

## 実施内容(詳細 - 検証)

### 検証1:リトライ後、SQSのメッセージがデッドレターキューに入ることを確認する
以下の手順でSQSでメッセージを送信してみます

- SQSで`sample-message-queue`を選択し、`メッセージを送受信`をクリック
- メッセージ本文に`test`と入力する
- 配信遅延時間：デフォルト(=0秒)とする
- メッセージを送信

すると、`sample-message-queue`で処理中のメッセージが1つあることがわかります。

![01 19.21.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882682/a685219d-a983-c6c5-c312-0e040fe79705.png)

数分後に、CloudWatchログを確認すると、最大受信数分だけLambdaがリトライ処理をしていることがわかります。

![cloudwatch logs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882682/df052279-f803-a855-a085-01bc9458b5b1.png)

SQSをみると、`sample-message-queue-dlq`にメッセージが移動していることがわかります。

![02 19.21.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882682/ea331193-6319-f5a4-5745-ef898ac3c41d.png)

次に、以下手順により、先ほど送ったメッセージを取得できていることを確認できます。

- `sample-message-queue-dlq`を開き、`メッセージを送受信`を押す
- `メッセージをポーリング`を押す
- 先ほど送ったメッセージを取得し、`test`と書かれたメッセージを確認できる

スクリーンショットを取得し忘れましたが、、上記手順で確認できるはずです。

### 検証2:デッドレターキューに入ったメッセージを使って再処理できることを確認する

検証1でデッドレターキューにメッセージが入り、中身を確認することができました。

次に、このメッセージを再度処理してみましょう。

以下手順でメッセージを再処理できます。

- `sample-message-queue-dlq`を開き、`DLQ再処理の開始`を押す
- `メッセージの送信先:再処理のためにカスタム送信先に移動`を押す
- 既存のキュー：`sample-message-queue`を押す
- `DLQ再処理`を押す

上記により、先ほど`sample-message-queue-dlq`に入っていたメッセージが、`sample-message-queue`に移動していることがわかります。

![03 19.21.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882682/aec43be3-c0e4-0eba-a6a1-0807836011b3.png)

再度CloudWatch logsをみると、最大受信数分だけLambdaがリトライ処理をしていることがわかります。

![Cloudwatch logs sqs retry.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882682/fb94a62b-20cd-48b7-2fd1-22b979ade685.png)

## まとめ
今回はSQSのデッドレターキューを理解するために、SQS＋Lambdaによりリトライ処理を実装してみました。
自分で手を動かして実装したことで、SQSについて理解が深まりました。
今後もAWSの他サービスやプログラミングに関する検証系のブログ記事を書いていこうと思います。

## 参考
https://qiita.com/yoshiyama_hana/items/665ff4b949c4e7e96ad2