Amazon SNSでメッセージのバッチ(一括)送信出来るようになっていたのでためしてみた

2022.01.03

いわさです。

Amazon SNSトピックを使ってメッセージ発行を行うことでシステム間の連携を行うことが出来ますが、少し前にメッセージ発行時のバッチ処理が出来るようになっていました。

従来のメッセージ発行はpublishサブコマンドを使っていましたが、AWS CLI v2.4.0でpublish-batchサブコマンドが新たに実装されています。
このコマンドを使うと最大10件までのメッセージを一括送信することが出来ます。
SNSの従量課金の一部はAPIリクエスト数による料金が含まれているので、バッチ送信をうまく活用できるとその部分が最大90%コスト減になります。

本日はこちらを試してみましたので紹介したいと思います。

Publish

まずは通常の発行の流れをおさらいしたいと思います。
今回は、Amazon SQSをサブスクライブさせています。

トピックを指定してメッセージをpublishします。

iwasa.takahito@hoge ~ % aws sns publish --topic-arn arn:aws:sns:ap-northeast-1:123456789012:20220103sns --message test                                         
{
    "MessageId": "291403e7-7de9-59d2-b0a0-59abe0b6bb44"
}

SQSキューで受信確認をしてみます。

iwasa.takahito@hoge ~ % aws sqs receive-message --queue-url https://sqs.ap-northeast-1.amazonaws.com/123456789012/20220103sqs | jq '.Messages[].Body | fromjson' | jq -r '.Message'
test

受信出来ていますね。

PublishBatch

バッチ送信を行う場合は、publish-batchサブコマンドを使いますが、publish-batch-request-entriesパラメータにリスト形式でメッセージを最大10件指定します。
バッチ送信時の注意点としてはペイロードサイズが合計256KB以内である必要があります。publishサブコマンドでも最大は256KBでしたが、publish-batchだと合計で256KBが最大です。この点は注意が必要ですね。
また、サービスクォーターのMessages Published per Secondについてはメッセージ数になるので仮に10件を一括送信するAPIコール数だと3000回になります。APIコール数は削減出来ますが、メッセージ送信数が減るわけではないのでその点も注意が必要です。

標準トピック

SNSはトピックが2種類あって、標準トピックとFIFOトピックがあります。
FIFOと聞くとSQSを想像すると思いますがSNSにもあり、SQSのFIFOキューでサブスクライブする場合はSNS側もFIFOである必要があります。
一括処理での送信順序は保証されていません。後述しますが、順序を保証させたい場合はFIFOトピックを使う必要があります。

まずは標準トピックで試してみます。

iwasa.takahito@hoge ~ % aws sns publish-batch --topic-arn arn:aws:sns:ap-northeast-1:123456789012:20220103sns --publish-batch-request-entries '
[
    {"Id": "1", "Message": "test1"},
    {"Id": "2", "Message": "test2"},
    {"Id": "3", "Message": "test3"},
    {"Id": "4", "Message": "test4"},
    {"Id": "5", "Message": "test5"},
    {"Id": "6", "Message": "test6"},
    {"Id": "7", "Message": "test7"},
    {"Id": "8", "Message": "test8"},
    {"Id": "9", "Message": "test9"},
    {"Id": "10", "Message": "test10"}
]' 
{
    "Successful": [
        {
            "Id": "1",
            "MessageId": "a3940f45-3368-5c0f-98c0-5509acc32373"
        },
        
        ...
        
        {
            "Id": "10",
            "MessageId": "7457572c-a931-5516-891f-0d60369a0a6a"
        }
    ],
    "Failed": []
}

では受信確認してみます。

iwasa.takahito@hoge ~ % aws sqs receive-message --queue-url https://sqs.ap-northeast-1.amazonaws.com/123456789012/20220103sqs --visibility-timeout 300 | jq '.Messages[].Body | fromjson' | jq -r '.Message'
test4
...
test8
...
test9
...
test3
...
test7
...
test10
...
test5
...
test1
...
test2
...
test6

順序はバラバラですが、10件送信できていますね。

ちなみに、11件以上指定した場合は、一部送信されて11件目がFailedになるわけではなく、バッチAPIコール全体が失敗します。

iwasa.takahito@hoge ~ % aws sns publish-batch --topic-arn arn:aws:sns:ap-northeast-1:123456789012:20220103sns --publish-batch-request-entries '
[
    {"Id": "1", "Message": "test1"},
    {"Id": "2", "Message": "test2"},
    {"Id": "3", "Message": "test3"},
    {"Id": "4", "Message": "test4"},
    {"Id": "5", "Message": "test5"},
    {"Id": "6", "Message": "test6"},
    {"Id": "7", "Message": "test7"},
    {"Id": "8", "Message": "test8"},
    {"Id": "9", "Message": "test9"},
    {"Id": "10", "Message": "test10"},
    {"Id": "11", "Message": "test11"}
]'

An error occurred (TooManyEntriesInBatchRequest) when calling the PublishBatch operation: The batch request contains more entries than permissible.

FIFOトピック

次の順序を保証させたい際を想定してFIFOトピックを使ってみましょう。
ちなみに、標準トピックは様々な送信先が設定出来たと思いますが、FIFOトピックについてはSQSのみです。

先程と同じくpublish-batchサブコマンドを使用するのですが、メッセージにMessageGroupIdを含める必要があります。
このMessageGroupIDはSQS FIFOキューにおけるメッセージグループと同じです。
また重複排除のために、MessageDeduplicationIdを指定するか、トピックの「コンテンツベースのメッセージ重複排除」を有効化するか、どちらかが必要です。(※今回は後者)

iwasa.takahito@hoge ~ % aws sns publish-batch --topic-arn arn:aws:sns:ap-northeast-1:123456789012:20220103sns.fifo --publish-batch-request-entries '
[
{"Id": "400001", "Message": "test1", "MessageGroupId": "4001"},
{"Id": "400002", "Message": "test2", "MessageGroupId": "4002"},
{"Id": "400003", "Message": "test3", "MessageGroupId": "4003"},
{"Id": "400004", "Message": "test4", "MessageGroupId": "4004"},
{"Id": "400005", "Message": "test5", "MessageGroupId": "4005"},
{"Id": "400006", "Message": "test6", "MessageGroupId": "4006"},
{"Id": "400007", "Message": "test7", "MessageGroupId": "4007"},
{"Id": "400008", "Message": "test8", "MessageGroupId": "4008"},
{"Id": "400009", "Message": "test9", "MessageGroupId": "4009"},
{"Id": "400010", "Message": "test10", "MessageGroupId": "4010"}
]'
{
    "Successful": [
        {
            "Id": "400001",
            "MessageId": "faf05727-1468-57ea-b40b-0d6bbcfea876",
            "SequenceNumber": "10000000000000009000"
        },

        ...

        {
            "Id": "400010",
            "MessageId": "ad0a5333-f45c-5d09-8393-097f5d11e3cd",
            "SequenceNumber": "10000000000000010006"
        }
    ],
    "Failed": []
}

レスポンスを確認すると、パラメータに設定した順序でSequenceNumberが割り当てられています。
SQSで受信確認してみます。

iwasa.takahito@hoge ~ % aws sqs receive-message --queue-url https://sqs.ap-northeast-1.amazonaws.com/123456789012/20220103sqs.fifo --max-number-of-messages 10
{
    "Messages": [
...
            "Body": ..."test1\"
            "Body": ..."test2\"
            "Body": ..."test3\"
            "Body": ..."test4\"
            "Body": ..."test5\"
            "Body": ..."test6\"
            "Body": ..."test7\"
            "Body": ..."test8\"
            "Body": ..."test9\"
            "Body": ..."test10\"
...
    ]
}

指定した順序で送信されました。

さいごに

本日は、Amazon SNSの一括送信処理を使ってみました。
メッセージをまとめたり、利用側の修正が必要になりますが、冒頭で触れたとおり、うまく活用できればAPIリクエスト数を削減出来る可能性があります。
もし、APIリクエスト数が多くてネットワークリソースやリクエスト数によるコストでお悩みの場合は利用出来るか検討してみてください。