SQSのバッチアクションAPI (boto3) の効果を確認してみた

SQSのバッチAPIによるメッセージ送信の改善効果と、KeepAliveの無効化に伴う性能影響の確認を試みてみました。
2023.10.02

Amazon SQS と SNSでは、最大10個のメッセージの同時発信をサポートするAPIが提供されています。

今回、Python用のAWS SDK、Boto3を利用して、SQSのメッセージ送信をバッチ処理するAPI、SendMessageBatch の 効果を確認する機会がありましたので、紹介させていただきます。

検証環境

  • Amazon Linux 2023 (ARM) のAMI、「c7g.large」の EC2インスタンスを起動しました。
$  uname -a
Linux ip-172-31-2-138.us-west-2.compute.internal 6.1.52-71.125.amzn2023.aarch64 #1 SMP Tue Sep 12 21:41:10 UTC 2023 aarch64 aarch64 aarch64 GNU/Linux
$ pip list|grep boto3
boto3                1.28.57
$ python --version
Python 3.9.16
  • Python3.9、pip、boto3 のインストールを実施、Boto3の実行環境として利用しました。
sudo dnf install python pip -y
pip3 install boto3

デミーデータ作成

  • OpenSSLコマンドを利用、1行約25KB、1000件のレコードを用意しました。
  • 10レコードのサイズは約250KB。 SQSのバッチAPIの上限、256KBに収まる容量としました。
for ((i=0; i<1000; i++))
do
  openssl rand -hex 13000 >> 1.tmp
done

Pythonスクリプト

3種類のPythonスクリプトを用意。 1000メッセージの送信所要時間より、毎秒送信可能なスループットを求めました。

KeepAlive無効

ループ中に都度SQSのコネクションを再接続する、KeepAlive無効なSQSクライアント(プロデューサー)を再現したコードを用意しました。

import boto3
queue_url="https://sqs.us-west-2.amazonaws.com/00000000/test-sqs"
f = open('1.tmp', 'r')
for a in f:
  sqs = boto3.client('sqs', region_name='us-west-2')
  response = sqs.send_message(
    QueueUrl=queue_url,
    MessageBody=(a)
  ) 
  sqs.close()
f.close()

通常処理

SQSのコネクションを再利用、1000回 send_message を実行するコードを用意しました。

import boto3
sqs = boto3.client('sqs', region_name='us-west-2')
queue_url="https://sqs.us-west-2.amazonaws.com/00000000/test-sqs"
f = open('1.tmp', 'r')
for a in f:
  response = sqs.send_message(
    QueueUrl=queue_url,
    MessageBody=(a)
  )  
f.close()

バッチAPI

バッチAPI (send_message_batch) を利用。 10件のメッセージを一括して送信する処理のコードを用意しました。

import boto3
sqs = boto3.client('sqs', region_name='us-west-2')
queue_url="https://sqs.us-west-2.amazonaws.com/00000000/test-sqs"
f = open('1.tmp', 'r')
i=0
c=[]
for a in f:
  i+=1
  b = {'Id': str(i), 'MessageBody': a}
  c.append(b)
  if len(c) >= 10:
    response = sqs.send_message_batch(
        QueueUrl = queue_url,
        Entries = c
    )
    c=[]
f.close()

結果

boto3-sqs-batch-api

  • バッチAPI、10件のメッセージを一括送信する事で、1.76倍のスループット向上が確認できました。
  • SQSのコネクションが再利用出来ない場合には、大きくスループットが低下する事が確認できました。
API スループット(rps)
send_message (都度接続) 15.71
send_message 71.57
send_message_batch 126.47

まとめ

SQS、SNS のメッセージ 送信(プロデューサー)、大量、かつ高いスループットで処理する必要がある場合、 まずバッチAPIの活用をご検討ください。

また、頻繁すぎるSDKの初期化、コネクション設定での利用は、スループットに悪影響を及ぼす場合があります。 特に実行環境として Lambda を利用する場合、初期化コストの高い初期化などの処理は、なるべくハンドラー外で設定いただくことをおすすめします。

CLI

AWS CLIでも、SQSのバッチAPIが効果的である事が確認できました。

send-message

for ((i=0; i<1000; i++))
do
  openssl rand -hex 13000 >> 1.tmp
done

time cat 1.tmp | while read a
do
  aws sqs send-message  --queue-url ${QueueUrl}  --message-body ${a} > /dev/null 2>&1
done

real    16m42.909s
user    12m51.354s
sys     3m3.881s

send-message-batch

TMP1=$(echo '{}' | jq ".QueueUrl|=\"${QueueUrl}\"" | jq ".Entries |= []")
TMP2=${TMP1}
for ((i=0; i<1000; i++))
do
  TMP2=$(echo ${TMP2} | jq ".Entries |= .+[{\"Id\": \"${i}\", \"MessageBody\": \"`openssl rand -hex 13000`\" }]" )

  if [ `echo ${TMP2} | jq '.Entries| length' ` -eq 10 ] ; then
    echo ${TMP2} | jq . -c > ${i}.json
    TMP2=${TMP1}
  fi

done

time  ls -1 *.json | xargs -P 1 -I{} aws sqs send-message-batch --cli-input-json file://./{}  > /dev/null 2>&1
real    0m50.116s
user    0m38.918s
sys     0m6.713s

time  ls -1 *.json | xargs -P 2 -I{} aws sqs send-message-batch --cli-input-json file://./{}  > /dev/null 2>&1
real    0m25.689s
user    0m39.448s
sys     0m7.124s