LoginSignup
35
8

More than 3 years have passed since last update.

Amazon SQS ロングポーリングを理解する

Last updated at Posted at 2020-03-08

概要

Amazon SQSのFAQを見れば理解出来ますが、ちょっと分かりづらいところもあるので、自分用にまとめ直します。

(参考)よくある質問 - Amazon SQS | AWS
https://aws.amazon.com/jp/sqs/faqs/

結論

ロングポーリングを使う(ショートポーリングは使わない)。ロングポーリングタイムアウトは20秒(最大値)を設定する。

個人的に思った疑問と回答

*メッセージがキューに入った後、取り出せるのは最大20秒後になる?

No.
下記の通り、メッセージがキューに入った後すぐ取り出せる。(レイテンシはあるが)

ロングポーリングリクエストを使用すると、メッセージがキューに達した時点ですぐに、キューの消費コンポーネントがそのメッセージを受信でき、また ReceiveMessageResponses インスタンスが空で返される回数が減ります。

複数メッセージが短時間にキューに入ったケースも検証しましたが、問題なくすぐ取り出せていました。

*キューが空の場合のReceiveMessageの挙動は?

ショートポーリングの場合

すぐに空のメッセージが返される

ロングポーリングの場合

ロングポーリングタイムアウト秒(最大20秒)ウエイトした後、空のメッセージが返される

SQSはリクエスト課金なので、出来るだけリクエスト数を少なくした方が良い。つまり、ロングポーリングタイムアウト秒を最大に設定することで、リクエスト回数を減らし、コストを抑えることが出来る。

*ショートポーリングの使いどころは?

ほとんどのユースケースで、Amazon SQS ロングポーリングはパフォーマンスの向上とコスト削減につながります。ただし、アプリケーションが ReceiveMessage 呼び出しから直ちに応答を受け取る必要がある場合は、アプリケーションに多少の変更を加えないとロングポーリングのメリットを活用できない可能性があります。

たとえば、複数のキューを単一スレッドでポーリングするアプリケーションの場合は、ショートポーリングからロングポーリングへの切り替えが機能しない可能性があります。単一スレッドは、すべての空のキューでロングポーリングタイムアウトを待機し、メッセージが含まれる可能性のあるキューの処理が遅れるためです。

このようなアプリケーションで複数のキューを処理する場合は、単一スレッドを使用しないようにすることができます。これにより、そのアプリケーションで Amazon SQS ロングポーリングのメリットを活用できるようになります。

2つのキュー(キューAとキューB)からメッセージを順番に取り出してシングルスレッドで処理するようなアプリケーションを想定します。
t=0[sec]でキューAに対してロングポーリングした場合、キューAが空の場合に最大で20秒待つことになるので、その間にt=10[sec]でキューBにメッセージが入っていても処理できません(t=20[sec]でキューAから取り出されます)。このような遅れを防ぐため、ショートポーリングは使われるのではないかと思います。
ただ、そもそもアプリケーションの作りが悪いですよね。もし、2つのキューのメッセージがお互いに依存しないで処理できるなら、アプリケーションを2つのスレッド(またはプロセス)に分ければロングポーリングにすることが可能です。

参考(SQSにアクセスするLambda関数)

動かす場合は、SQSへアクセスするポリシーを付与するのを忘れずに。

送信側

import json
import boto3

def lambda_handler(event, context):
    # TODO implement
    sqs = boto3.client('sqs')
    queue_url = sqs.get_queue_url(QueueName='testqueue.fifo')
    response = sqs.send_message(QueueUrl=queue_url["QueueUrl"], MessageBody='world3', MessageGroupId='group')

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!'),
        'queue_url': queue_url["QueueUrl"],
        'response': response
    }

受信側

WaitTimeSecondsを0にするとショートポーリング、1以上20以下にするとロングポーリング。

import json
import boto3

def lambda_handler(event, context):
    # TODO implement
    sqs = boto3.client('sqs')
    queue_url = sqs.get_queue_url(QueueName='testqueue.fifo')
    message = sqs.receive_message(QueueUrl=queue_url["QueueUrl"], WaitTimeSeconds=20)
    isMessageExists = message.get("Messages")
    if isMessageExists:
        response = sqs.delete_message(QueueUrl=queue_url["QueueUrl"], ReceiptHandle=message["Messages"][0]["ReceiptHandle"])
    else:
        response = ""

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!'),
        'message': message,
        'response': response
    }

追記

receive_messageのパラメータとしてWaitTimeSecondsを設定する方法の他に、キュー属性のReceiveMessageWaitTimeSecondsを設定する方法もあります。
https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html

35
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
8