【AWS公式ドキュメントを噛み砕く】SQSとは？

# はじめに
案件で触れるから、学習がてらアウトプットしてみるよ。（本は無いし、AWSの公式ドキュメントは読みにくいし・・・）
個人の理解メモなので、正しい情報はAWSの公式ドキュメント見てくださいね。
（そして誤りあれば指摘してもらえると嬉しいです）

# 概要
「[ここ](https://aws.amazon.com/jp/sqs/)」の話

## 利点
なんか、文章を箇条書きに変えただけになった。

**管理オーバーヘッドの排除**

- 可用性が高くスケーラブル！（キューが動的に作成され自動的にスケールされる）
- 構築/メンテナンスが楽！（前払い料金、メッセージングソフトウェアの取得、インストール、設定が不要）

**確実なメッセージ配信**

- どんな量のデータでもあらゆるレベルのスループットで転送可能！
- 転送時にメッセージが失われることも無い！
- システムの全体的な耐障害性が向上！（アプリと切り離されるため、実行や失敗を分離できる）
- 各メッセージの複数のコピーが複数のアベイラビリティーゾーンで冗長的に保存される！


**機密データの保護**

- 通信データは暗号化するよ！
- PCIDSSにも対応できるほどセキュア！（AWS Key Management Serviceを利用するのが望ましい）
- 規則やコンプライアンスの要件を満たせる！（暗号化キーが使用されるたびに AWS KMS によって AWS CloudTrail に記録される）

**伸縮自在にコスト効率良くスケールする**

- 容量計画や事前プロビジョニングについて心配する必要がありません！（自動でよしなにしてくれる）
- 標準キューではほぼ無制限のスループットが提供されます。
- 安い！（使用量に基づいて課金されるから）

なんか、営業文句っぽくて、本当に・・・？とか思ってしまう。

# 特徴
「[ここ](https://aws.amazon.com/jp/sqs/features/)」の話。
日本語未サポートやんけ！

## Queue types（キューの種類）
2種類あるらしい。現時点でFIFOキューは、東京/大阪リージョンでは使えないっす。

- Standard Queues（標準キュー）
    - Unlimited Throughput（無制限のスループット）：APIリクエストあたりのTPSは**ほぼ**無制限。（nearlyって書いてあった）
    - At-Least-Once Delivery（最低一回のデリバリー）：たまに複数回配信することがある。
    - Best-Effort Ordering（順序保証はベストエフォート方式）：たまに順序が前後するよ。
- FIFO Queues（FIFOキュー）
    - High Throughput（高いスループット）：300TPS。（なんか補足説明あるけどまだ良くわからん）
    - Exactly-Once Processing（一度だけのプロセッシング）：複数回配信することは無いよ。
    - First-In-First-Out Delivery（FIFOを保証）：FIFOを守るよ。

## Functionality（機能）

- **Unlimited queues and messages**
    - 無制限！
- **Payload Size**
    - 任意の形式のテキストを最大256KBまで含めることが可能。
    - 課金はチャンク数に応じて決まるよ。（64KBを1チャンクとして扱うよ。）
- **Batches**
    - 最大10個のバッチに対応しているよ。（バッチが何かは現時点では良く分からん）
- **Long polling**
    - キューが空の時、ポーリング間隔を最大20秒にする。（不必要なポーリングを減らすことで、コストを最小限に抑える）
- **Retain messages in queues for up to 14 days.**
    - キューの中身は14日間保存。
- **Send and read messages simultaneously.**
    - 送信と読込を同時に行える
- **Message locking**
    - 処理中のメッセージはロックされるので、メッセージが二重に処理されることは無い。
    - 処理に失敗したらロックは外れる。
- **Queue sharing**
    - いろんな枠組みでキューの内容を共有できる
        - 匿名もしくは任意のAWSアカウント
        - IPアドレス
        - 時間帯
- **Server-side encryption (SSE)**
    - AWS Key Management Serviceを使用して、暗号化できるよ。
    - メッセージを受信するとすぐにメッセージを暗号化する。
    - メッセージは暗号化された形式で保存
    - 許可されたコンシューマに送信されたときにのみメッセージを復号
- **Dead Letter Queues (DLQ)**
    - 本体のキューとは別に、デッドレターキュー（DLQ）と呼ばれるキューが用意可能。
    - 最大受信カウントを超えると、本体のキューではなく、DLQに移動する。
    - DLQに対して個別の処理を用意しておけば、メッセージが滞っている理由を分析して理解するのに役立つ。
    - DLQは、本体のキューと同じキュータイプ（標準orFIFO）であるべき。

# 開発者ガイド
日本語対応してあるぜ！やったぜ！

## Amazon SQSの仕組み
「[ここ](https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-how-it-works.html)」の話

### 基本的なアーキテクチャー
**分散キュー**
基本的な考え方としては、以下の3つの役割が存在する。

- 分散システムのコンポーネント：要は、自分で作るアプリケーション？
- キュー (Amazon SQS サーバーで分散)：キューそのもの
- キューのメッセージ：メッセージ。

わかりやすい図と説明が公式にあるので、これは見たほうが早い・・・
[ここ](https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-basic-architecture.html)

ポイント

- キューにメッセージを送ると、SQSサーバー全体で冗長化される。（複数のキューに、同じメッセージが入る）
- メッセージは処理されている間キューに残る。また、可視性タイムアウトの間は次の受信リクエストに返されることは無い。
- 可視性タイムアウトの期限が切れると、キューからメッセージ A を削除してメッセージが再び受信されて処理されるのを回避する。

疑問

- 可視性タイムアウトを設けているのは何故だ？

### スタンダートキュー

前段で触れた「特徴」と、ほとんど同じ内容なのでだいたい割愛。
ショートポーリングの話だけ初見なので、書いとく。

**ショートポーリングを使用したメッセージの処理**
ショートポーリングを使うか、ロングポーリングを使うかを選べるらしい。
以下は、ショートポーリングを使う場合の注意事項とか。

- 受信リクエストに対して、すべてのメッセージが返されないことがある。
    - サーバーを分散していて、メッセージを抽出する対象にするかどうかは、AWSのロジックに基づいて判定される。
    - 対象となったサーバーだけでは、全てのメッセージを抽出できない場合があるらしい。
- キューから処理し続ければ、Amazon SQS によりすべてのサーバーがサンプリングされ、すべてのメッセージを受信する。

だから、順序性が保証できないのね。

### FIFOキュー
東京/大阪リージョンでは使えないっぽいし、割愛。

### キューとメッセージの識別子

**受信ハンドル**

- メッセージを削除したり、メッセージ可視性を変更したりするには、受信ハンドル (メッセージ ID ではなく) を指定する必要がある。
- 受信ハンドルは、キューからメッセージを受信するたびに受け取る。
- 受信ハンドルの最大長は 1024 文字。

つまり、メッセージを削除する前にメッセージを受信する必要がある (メッセージをキューにおいてから回収することはできません)。

**メッセージ属性**
「メッセージ」が出てきすぎて、ゲシュタルト崩壊してる。。。

- メッセージに構造化メタデータ (タイムスタンプ、地理空間データ、署名、識別子など) を含めることができます。
- メッセージ属性はオプションであり、メッセージ本文とは別個のものです (ただし、メッセージ本文とともに送信されます)。
- メッセージ属性を使用して、最初にメッセージ本文を処理することなく、特定の方法でメッセージを処理できます。
- 各メッセージには最大 10 個の属性を指定できます。

**1つあたりのメッセージ属性の構成**

- Name
    - メッセージのすべての属性名で一意である必要があります
- Type
    - Valueのデータ型を指定するっぽい
    - 文字列、数値、バイナリが指定可能。他にも、「カスタム」ってのがあるけど、ここでは割愛。
- Value
    - 値

**注意事項**

- メッセージ属性のすべてのコンポーネントは、256 KB というメッセージサイズの制限に含まれます。
- Name、Type、Value、およびメッセージ本文を空または Null にすることはできません。

**メッセージ属性の MD5 メッセージダイジェストの計算**

まぁ正常に受信/送信できたかの検証のためにMD5検証をしましょうね、という話らしい。
JavaのSDK使えば割愛できるっぽい。
（どのタイミングでやるんだろう？受信時？）

### Cost Allocation Tags
コスト管理を容易にするために、キューにタグ付けができるらしい。

### ロングポーリング

なんか説明がわかりづらいけど、「ロングポーリング」「ショートポーリング」ってのは明確に分かれているものでは無い。
ただ、ポーリング間隔の設定値（ReceiveMessage）を長くしたら、「ロングポーリング」と言うだけの話。
そして、おそらくだけど、AWS的には基本的にロングポーリングを推奨したいから、わざわざ名前までつけたのだと思われる。（詳細は、もう少し下のベストプラクティス側に書きます。）

メリット

- レスポンスの送信前にメッセージがキューで使用可能になるまで Amazon SQS が待機できるように、空のレスポンス数を削減します。
- サブセットではなく、すべての Amazon SQS サーバーにクエリを実行して、偽の空のレスポンスを排除します。
- 利用可能になるとすぐにメッセージを返します。

「ベストプラクティスについては、[ロングポーリングのセットアップ](https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/working-with-messages.html#setting-up-long-polling) を参照してください。」とのこと。

### デッドレターキュー
正常に処理 (消費) できないメッセージの送信先として、他のキュー (ソースキュー) が使用することができるキューのこと。
問題のあるメッセージを分離して、処理が成功しない理由を調べることなどを目的にして存在している。

**デッドレターキューのしくみ**
なんかデッドレターキューに移動する条件はいろいろ指定できるらしい。
よくあるのは、「maxReceiveCount」が指定回数以上を超えたら、ってのらしい。
あと、デッドレターキューの保持期間を元のキューの保持期間よりも長く設定しよう！理由は以下。

> メッセージの有効期限は、常に元のキュー追加のタイムスタンプに基づいています。メッセージが dead-letter queue に移動されたとしても、キュー追加のタイムスタンプは変更されません。
> したがって、デッドレターキューの保持期間を元のキューの保持期間よりも長く設定することが､常にベストプラクティスとなります。

**キューの種類によるメッセージエラーの処理**
スタンダードキューだけ触れます。

- 保持期間が終わるまでメッセージの処理が継続されます。
- スタンダードキューでは、多数の保留メッセージが許容されます。
    - ただし、多数の保留は、有効なメッセージの処理速度が低下する可能性があります。
    - キューの効率を維持するには、アプリケーションでメッセージを正しく処理できているか確認するべき。

**デッドレターキューが適している用途/適していない用途**

適している

- スタンダードキューとの組み合わせ
- メッセージ数を減らし、ポイズンピルメッセージ（要は、上述の保留メッセージ）の発生を抑える

適していない

- メッセージの送信を無期限に再試行できる設定にしている場合
- 操作の正確な順序を維持する必要がある場合（FIFOキューとか）

**デッドレターキューのトラブルシューティング**

- トラブル：AWS マネジメントコンソールを使用してメッセージを表示すると、メッセージがデッドレターキューに移動されることがある
    - 対応するキューの Redrive ポリシーで、[Maximum Receives] 設定の値を大きくします。
    - 対応するキューのメッセージが AWS マネジメントコンソールに表示されないようにします。
- トラブル：デッドレターキューの NumberOfMessagesSent と NumberOfMessagesReceived が一致しない
    - そういうもんです

### 可視性タイムアウト

スタンダード キューの場合は、可視性タイムアウトはメッセージを 2 回受信しない保証にはならない。

**インフライトメッセージ**
インフライト（飛行中）ってのは、アプリケーションがキューからメッセージを受け取って、削除するまでの間を指す。

【制限】
スタンダード キューの場合、キューには最大 120,000 のインフライトメッセージが存在できる。
制限に達した場合、Amazon SQS は OverLimit エラーメッセージを返します。

【対策】
処理されたメッセージはキューから削除すること。
メッセージの処理に使用するキューの数を増やす。
制限の引き上げをリクエストする。（サポートリクエスト）

**可視性タイムアウトの設定**

- 可視性タイムアウトは Amazon SQS がメッセージを返した時点で始まります。
- DeleteMessage アクションが呼び出されないまま可視性タイムアウトの期限が切れると、そのメッセージは他のコンシューマーに見えるようになり、再度受信されます。
- メッセージを一度だけ受信する必要がある場合、コンシューマーは可視性タイムアウトの時間内にメッセージを削除する必要があります。
    - ただし、2回受信しない保証にはならない。
    - アプリケーションがキューのメッセージを処理して削除するまでの最大所要時間に、可視性タイムアウトを設定します
    - メッセージの処理にかかる時間がわからない場合は、コンシューマープロセスのハートビートを作成します。
        - コンシューマーがメッセージを処理し続けている限り、可視性タイムアウトの毎秒、指定時間ずつ延長します。

## ベストプラクティス
「[ここ](https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-standard-fifo-queue-best-practices.html)」の話。
FIFOキュー特有の話は割愛してます。

### メッセージの操作
**タイムリーな方法でのメッセージの処理**

- 処理時間が分かっているときは、不必要に長い時間「可視性タイムアウト」を設定しておく必要はない。
- 処理時間が不透明な場合は、ハートビート機能を使おう。

**リクエストエラーの処理**

- SDKを使おう（自動リトライの仕組みが含まれている）
- そうじゃないときはガイドに従って自分で実装せよ。

**ロングポーリングのセットアップ**

- 空のレスポンスと、偽の空のレスポンスの数を削減するために、ロングポーリング機能を使おう。
    - ほとんどの場合、ReceiveMessage 待機時間を 20 秒で良い。
    - 複数のキューにロングポーリングを実装する場合は、キューごとに 1 つのスレッドを使用しよう。

「[FAQ](https://aws.amazon.com/jp/sqs/faqs/)」に記載があるんですが、ほとんどのケースでロングポーリングが推奨されます。
じゃぁショートポーリングはどういうケースで使うの？って話だけど、AWS側も思いつかなかったのか、何も記載がない。

ショートポーリングの場合、レスポンスを早く返すため、キューにメッセージが残っていても、「何もないよ！」ってレスポンスを返しがち。
そもそも、キューが分散しているので、「何もないよ！」とレスポンスしているくせに、アクセスできてないキューにメッセージが残っているケースがある。
そこで、アクセスするキューの数をあげるには、「ポーリング間隔(ReceiveMessage)」を長くする必要がある。

と言うことで、「20秒（Max値）」にしよう＝ロングポーリングしよう、とAWSは言っているだけの様子。

ロングポーリングにしたからと言って、「20秒ずっとレスポンスが返ってこない」と言う事はなく、メッセージがみつかれば即座にレスポンスが返ってきます。
（見つからなければ20秒かかります。）

20秒かけたからといって、全てのキューを見れると言う保証はないため、ロングポーリングにしたからといって、「キューにメッセージが残らなくなる」と言うわけではない。
ただ、ショートポーリングと比べたら、見にいくキューの数も増えるので、「キューにメッセージがあるのに、メッセージありませんでした！」って言うケース（偽の空のレスポンス）が減らせる。

そもそも、見にいく間隔自体が伸びるので、「どこにもメッセージはありませんでした！」って言うレスポンス（空のレスポンス ）も減らせる。

結局、ポーリングした回数だけ、課金されるので、それを減らすための方策として、ロングポーリングは当たり前だよーって話らしい。


**問題のあるメッセージのキャプチャ**

- デッドレターキューを使おう。
    - Redrive ポリシーを設定しよう。
        - ソースキューがメッセージの処理の失敗を指定回数繰り返した後に、デッドレターキューにメッセージをリダイレクトします。

**デッドレターキューの保持の設定**

- デッドレターキューの保持期間を元のキューの保持期間よりも長く設定しよう。

**メッセージ処理の不整合の回避**

- デッドレターキューの設定時に最大受信数を 1 に設定することは避けてください。
    - スタンダード キューによる整合性のないメッセージ処理を回避するため

**リクエストと応答システムの実装**

- メッセージごとに返信キューを作成しない。
    - 代わりに、起動時にプロデューサーごとに返信キューを作成し、相関 ID メッセージ属性を使用して返信をリクエストにマッピングします。
- プロデューサーで返信キューを共有しない。
    - 共有した場合、プロデューサーは他のプロデューサー向けの応答メッセージを受信する可能性があります。

### コストの削減
**メッセージアクションのバッチ処理**

- メッセージを送信、受信、および削除し、1 つのアクションで複数のメッセージのメッセージ可視性タイムアウトを変更するには、Amazon SQS バッチ API アクションを使用します。
- リクエストのバッチ処理によりクライアント側のバッファリングを結合するには、AWS SDK for Java に含まれているバッファされた非同期クライアントとともにロングポーリングを使用します。

**適切なポーリングモードの使用**

- ロングポーリングにより、利用可能になるとすぐに、Amazon SQS キューからメッセージを処理することができます。
- ショートポーリングは、ポーリングされた Amazon SQS キューが空の場合でも、すぐにレスポンスを返します。

# さいごに
だいたい2時間くらいで読み終わった。

以上