AWS最古のサービスSQSを今回は触ってみます。
SQS(Simple Queue Service)とは
Amazon Simple Queue Service(Amazon SQS)には、コンピュータ間で送受信されるメッセージを格納するための、信頼性の高いスケーラブルなホストされたキューが用意されています。Amazon SQS を使用することによって、さまざまなタスクを実行するアプリケーションの分散コンポーネント間で、データを簡単に移動させることができます。
キューイングサービスですね。どういう用途に使うかというと、例えば処理を切り離したい時に使う事が出来ます。
DBのデータ更新系の処理だとして、リクエストを受け付けて、その後DBの更新を行うとし、このDB更新に時間が掛かるとします。更新が完了するまでの間レスポンスが帰ってこない状態になってしまうと思います。これを受付と更新とで切り分ける時にキューイングの出番です。
受付処理の時にキューイングに更新依頼きたよーとキューにメッセージを入れます。キューにメッセージを入れたら、レスポンスを返却します。その後、ワーカーがキューのメッセージの有無を確認し、メッセージがあればDBを更新します。
キューイングを用いると処理を切り話す事が出来ます。
キューは、データの生成と保存を行うコンポーネントと、データを受信して処理を行うコンポーネントの間のバッファーとして機能します。つまりキューは、消費者(Consumer)の処理スピードよりも生産者(Producer)が早く作業を生成したり、生産者と消費者がネットワークに断続的に接続されている場合などに発生する問題を解決します。
AWS Black Belt Techシリーズは鉄板なので張っておきます。これを見れば間違いないです。
SQSの仕様(この情報は古い場合があります)
- キューに格納できるメッセージは、最大 256 KB
- キューには4日間(デフォルト)メッセージが保持される。ただし、メッセージが送信されてから最大14日間保持されるようにキューを設定できる。
- 連続する30 日間、SendMessage、ReceiveMessage、DeleteMessage、GetQueueAttributes、SetQueueAttributes、AddPermission、RemovePermission のいずれかのアクションが実行されていない場合、Amazon SQS は通知なしでキューを削除する事がある。
- 同一メッセージを配信する可能性がある(少なくとも1回の配信)
- 送信したのとまったく同じ順序でメッセージを受信する事は保証できない。 (FIFOではない)
- メッセージ受信動作にはショートポーリング、ロングポーリングがある。
- キューあたりの転送中のメッセージ数には、120,000という制限がある。
- メッセージ送信/受信は一度に最大10件
同一メッセージを配信する可能性がある(少なくとも1回の配信)ので、同じメッセージを複数回処理した場合に悪影響を出さないように、冪等性を意識したアプリケーション設計を行う必要がある。
メッセージ属性
メッセージ属性がサポートされます。メッセージ属性を使用すると、構造化メタデータ項目(タイムスタンプ、地理空間データ、署名、識別子など)などを指定することができます。メッセージ属性はオプションであり、メッセージ本文とは別個のものですが、同時に送信されます。この情報は、メッセージ本文を処理してからでなくてもメッセージを処理する方法を調べるために、メッセージの受信者が使用できます。各メッセージには最大 10 個の属性を指定できます。
各メッセージ属性は、次の項目で構成されています。
名前 – メッセージ属性名には、A-Z、a-z、0-9、下線(_)、ハイフン(-)、ピリオド(.)を使用できます。名前の先頭と末尾をピリオドにすることはできず、ピリオドを連続して使用することはできません。名前では大文字と小文字が区別され、メッセージのすべての属性名間で一致にする必要があります。名前の長さは最大 256 文字です。名前を "AWS." または "Amazon."(または大文字と小文字の違いによるバリエーション)で始めることはできません。これらのプレフィックスは、アマゾン ウェブ サービス(AWS)が使用するように予約されています。
型 – サポートされるメッセージ属性のデータ型は、文字列、数値、バイナリです。型には、カスタム情報を指定することもできます。データ型のコンテンツには、メッセージ本文と同じ制限があります。データ型では大文字と小文字が区別され、長さは最大 256 バイトです。詳細については、「メッセージ属性のデータ型と検証」セクションを参照してください。
値 – ユーザー指定のメッセージ属性値。文字列データ型の場合、値属性のコンテンツにはメッセージ本文と同じ制限があります。詳細については、「SendMessage」を参照してください。
名前、型、値を空または Null にすることはできません。さらに、メッセージ本文を空または Null することもできません。メッセージ属性のすべての部分(名前、型、値を含む)は、メッセージサイズの制限に含められます。制限は、現在のところ 256 KB(262,144 バイト)です。
メッセージに属性を設定する事が出来ます。これはAPIからでもコンソールからでも設定可能です。このメッセージ属性を用いて、FIFOを実現している記事を見つけましたので、リンクを張っておきます。
DelaySeconds(遅延キュー)
遅延キューを使用すると、キューにある新しいメッセージの配信を一定の秒数延期することができます。遅延キューを作成した場合、そのキューに送信したすべてのメッセージが遅延期間の間コンシューマーに表示されなくなります。DelaySeconds 属性を 0 ~ 900(15 分)の任意の値に設定することで、CreateQueue を使用して遅延キューを作成できます。SetQueueAttributes を使用してキューの DelaySeconds 属性を設定することにより、既存のキューを遅延キューに入れることもできます。
遅延キューは、メッセージを一定の時間コンシューマーが使用できなくするという点で可視性タイムアウトと似ています。遅延キューと可視性タイムアウトの違いは、遅延キューの場合、メッセージが最初にキューに追加されたときに非表示になるのに対して、可視性タイムアウトの場合、メッセージがキューから取得された後のみ非表示になるという点です。次の図は、遅延キューと可視性タイムアウトの関係を示しています。
メッセージがキューに追加されたタイミングで指定した秒数、非表示になります。後で紹介するVisibility Timeoutと似ていますが、Visibility Timeoutの場合、メッセージがキューから取得された後のみ非表示になります。
DelaySecondsは0 秒~15 分の間である必要があります。
メッセージタイマー
キューに追加するメッセージの初期非表示期間を指定することができます。たとえば、DelaySeconds パラメータが 45 に設定されたメッセージを送信した場合、メッセージがキューに入れられてから最初の 45 秒間はコンシューマーに表示されません。DelaySeconds のデフォルト値は 0 です。
個々のメッセージのメッセージタイマー設定は、遅延キューに適用されるどの DelaySeconds 値よりも優先されます。
DelaySeconds(遅延キュー)はキューの全てのメッセージ設定するのに対し、メッセージタイマーはメッセージに設定するものになります。DelaySecondsとメッセージタイマーでは、メッセージタイマーが優先されます。
Visibility Timeout(可視性タイムアウト)
コンポーネントがメッセージを受信した直後、そのメッセージはまだキューにあります。ただし、システム内の他のコンポーネントが、そのメッセージを再度受信および処理することは望ましくありません。したがって、Amazon SQS では、可視性タイムアウトによりそのコンポーネントがブロックされます。つまり、このタイムアウトの期間、他の消費コンポーネントがそのメッセージを受信および処理できなくなります。次の図と説明はこの概念を示しています。
たとえば、キューのタイムアウトが 30 秒で、そのキューからメッセージを受信するとします。そのメッセージのタイムアウトが 20 秒経過した時点(つまり、残り 10 秒)で、さらに 60 秒必要な場合、VisibilityTimeout を 60 秒に設定してメッセージに ChangeMessageVisibility をすぐに呼び出します。つまり、メッセージの可視性タイムアウトを 30 秒から 80 秒に延長したことになります(初期タイムアウト設定から 20 秒 + タイムアウトを変更してから 60 秒)。
メッセージの可視性タイムアウトを拡張すると、新しいタイムアウトはメッセージの特定の受信にのみ適用されます。ChangeMessageVisibility は、キューのタイムアウトや、メッセージの後続の受信には影響を与えません。何らかの理由でメッセージを削除せず、もう一度受信した場合、その可視性タイムアウトはキューに設定された元の値になります。
キューからメッセージを取得しても明示的にメッセージを削除しない限り、キューにはメッセージが残った状態になります。このメッセージを他の処理が取得してしまうと、同一の処理が行われ、整合性が取れない可能性があります。それを防ぐ為に、メッセージが取得されたら、他からそのメッセージを取得されない無いようにするのがvisibility Timeoutです。
Visibility Timeoutのデフォルトの秒数は30秒なので、30秒の間に処理を完了し、メッセージを削除する必要があります。そして、Visibility Timeoutは延長が可能なので、30秒以内に処理が完了しない場合は延長する事が出来ます。この延長はメッセージの受信にのみ適用されるので、元々の設定には反映されません。
Visibility Timeoutは0 秒~12 時間の間である必要があります
ショートポーリング
キューからメッセージを取得すると、Amazon SQS によりサーバーのサブセットがサンプリングされ (重み付けされたランダム分散に基づいて)、それらのサーバーのメッセージだけが返されます。つまり、ある受信リクエストによってすべてのメッセージが返されないことがあります。または、キューにあるメッセージが少ない(1000 通未満)の場合、あるリクエストによりメッセージがまったく返されない一方、後続のリクエストによりメッセージが返されることがあります。キューから取得し続けた場合、Amazon SQS によりすべてのサーバーがサンプリングされ、すべてのメッセージを受信できるようになります。
次の図は、システムコンポーネントの 1 つが受信リクエストを生成するとメッセージが返されるショートポーリングの動作を示しています。Amazon SQS は、複数のサーバー(灰色)をサンプリングし、それらのサーバーからメッセージ(メッセージ A、C、D、および B)を返します。メッセージ E はこの特定のリクエストに返されませんが、後続のリクエストには返されます。
つまりショートポーリングでは、サンプリング(キューからメッセージを取得する時)した際に、取得するメッセージに漏れが出てくるようです。
ロングポーリング
Amazon SQS キューに送信される ReceiveMessage リクエストへの返信で、返信できるメッセージがない場合に空のレスポンスの数が減ることです。ロングポーリングにより、レスポンスの送信前にメッセージがキューで使用可能になるまで Amazon SQS サービスが待機できるようになります。接続がタイムアウトしない限り、ReceiveMessage リクエストに対するレスポンスには、使用可能なメッセージが少なくとも 1 つと、ReceiveMessage 呼び出しでリクエストされた最大数以下のメッセージが含められます。
キュー(メッセージの入れ物)を作成する際に、WaitTimeSecondsを0~20秒の間で設定する事が出来ます。これを1~20秒で設定する事でロングポーリングの設定がされます。
ReceiveMessageを呼び出した時にWaitTimeSecondsで設定した秒数待機する事で、メッセージが空の場合でもメッセージが入ってくるまでWaitTimeSecondsの秒数まで待機し、その間にメッセージが入ってくれば取得する事が出来ます。
一方ショートポーリングはWaitTimeSecondsが0秒を設定する事でショートポーリングとなります。ショートポーリングでReceiveMessageを呼び出すとロングポーリングと違い、待機する事がないので、呼び出した時にメッセージがなければ取得する事はありません。
デットレターキュー
デッドレターキューは、何らかの理由で正常に処理できなかったメッセージの送信先として他の(送信元)キューが使用できるキューです。デッドレターキューを使用することの主なメリットは、正常に処理されなかったメッセージを対象から外して隔離することができることです。その後、デッドレターキューに送信されたメッセージを分析して試行し、正常に処理されなかった理由を調べることができます。
正常に処理が出来ず、何度もキューに戻ってきてしまうメッセージを隔離する事が出来ます。
ざっとSQSを試してみる
aws-shellを使って試してみます。便利です!
aws-shell
Queueの作成
aws> sqs --profile sqs_user create-queue --queue-name Myqueue
{
"QueueUrl": "https://ap-northeast-1.queue.amazonaws.com/123456789/Myqueue"
}
aws> sqs --profile sqs_user list-queues
{
"QueueUrls": [
"https://ap-northeast-1.queue.amazonaws.com/123456789/Myqueue"
]
}
Message送信
aws> sqs --profile sqs_user send-message --queue-url https://ap-northeast-1.queue.amazonaws.com/123456789/Myqueue --message-body example
{
"MD5OfMessageBody": "1a79axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"MessageId": "d5f68d9e-xxxx-xxxx-xxxx-xxxxxxxxxxxxxx"
}
Message受信
aws> sqs --profile sqs_user receive-message --queue-url https://ap-northeast-1.queue.amazonaws.com/123456789/Myqueue
{
"Messages": [
{
"Body": "example",
"ReceiptHandle": "AQEB8pBAGF7Lr9qN86IFOHNpog/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"MD5OfMessageBody": "1a79axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"MessageId": "d5f68d9e-xxxx-xxxx-xxxx-xxxxxxxxxxxxxx"
}
]
}
Message削除
aws> sqs --profile sqs_user delete-message --queue-url https://ap-northeast-1.queue.amazonaws.com/123456789/Myqueue --receipt-handle AQEB8pBAGF7Lr9qN86IFOHNpog/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
まとめ
改めてSQSを調べてみましたが、面白い作りをしていて勉強になる所が多かったです。Visiblity Timeoutの必要性も理解出来ましたし、注意する点も把握出来ました。
SQS単体で触ってみてもちょっとあれなので、実際にSQSを使ったアプリを作ってみようと思います。