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()
結果
- バッチ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