読者です 読者をやめる 読者になる 読者になる

PHPでSQSのFIFOキューを使った時のメモ

SQSのFIFOキューを使用する時に調べた事を記述します。

みんな大好きSQSは昔から存在する手軽に利用できるキューシステムですが、メッセージの順序保障と二重送信などはアプリ側で考慮する必要がありました。
しかし何時の間にやらFIFOキューとして機能が強化されていましたので調べてみた次第です

Amazon Simple Queue Service の新機能 – 1 回だけの処理と重複排除機能を備えた FIFO キュー | Amazon Web Services ブログ
※ただし、現在はUS East (Ohio) および US West (Oregon)でのみ利用可能な機能のようです。

まずはaws上で「オレゴン」リージョンにSQSのキューを作成します。
FIFOキューを選択肢します。

f:id:taka512:20170427190730p:plain

既にcomposerで以下のバージョンのawssdkを導入済とします

"aws/aws-sdk-php": "3.26.2"

以下のように送信処理を実装します。

// send.php
$sqs = SqsClient::factory([
    'credentials' => [
      'key' => YOUR_KEY,
      'secret' => YOUR_SECRET_KEY,
    ],
    'region' => 'us-west-2',
    'version' => '2012-11-05',
]);

$time = time();
for($i =0; $i < 10; $i++){
    $sqs->sendMessage([
        'QueueUrl'    => 'https://sqs.us-west-2.amazonaws.com/xxx/xxxxx.fifo',
        'MessageBody' => 'test body'.$i,
        'MessageGroupId' => 'group',
        'MessageDeduplicationId' => hash('sha256', $time.$i),
    ]);
}

受信処理は以下のように実装します。

// recv.php
$sqs = SqsClient::factory([
    'credentials' => [
        'key' => YOUR_KEY,
        'secret' => YOUR_SECRET_KEY,
    ],
    'region' => 'us-west-2',
    'version' => '2012-11-05',
]);
$result = $sqs->receiveMessage([
    'MaxNumberOfMessages' => 10,
    'QueueUrl' =>  'https://sqs.us-west-2.amazonaws.com/xxx/xxxxx.fifo',
]);
foreach ($result->search('Messages[]') as $message) {
    $queueHandle = $message['ReceiptHandle'];
    $messageBody = $message['Body'];
    echo $messageBody."\n";
    $sqs->deleteMessage([
        'QueueUrl' => 'https://sqs.us-west-2.amazonaws.com/xxx/xxxxx.fifo',
        'ReceiptHandle' => $queueHandle,
    ]);
}

実行結果

$ php send.php
$ php recv.php
test body0
test body1
test body2
test body3
test body4
test body5
test body6
test body7
test body8
test body9

ちなみにFIFOでない標準のキューを使用すると以下のようになります

$ php send.php
$ php recv.php
test body0
test body4
test body7
$ php recv.php
test body5
test body8
~略~


次に送信処理を以下のようにMessageDeduplicationIdが重複するように書き換えます

'MessageDeduplicationId' => hash('sha256', $time.$i),
↓
'MessageDeduplicationId' => hash('sha256', $time),

実行結果は以下のように最初の1行のみキューイングされます

$ php send.php
$ php recv.php
test body0

但し永遠に重複idは受付しないわけではなく、300秒間のインターバルで重複メッセージを排除するようです。

FIFO (First-In-First-Out) Queues - Amazon Simple Queue Service

キューの設定に以下のように「コンテンツに基づく重複排除」の選択肢があり、これをチェックすると「sendMessage」で「MessageDeduplicationId」の指定を省略できます。
MessageBodyの内容をsha256して自動で「MessageDeduplicationId」に設定してくれるようです。

f:id:taka512:20170427191537p:plain

「MessageGroupId」は同じグループでの順序性を保証するための値なので適当な文字列を設定するとよさげでした。