2026年5月30日
2026年5月30日
AWS SQSでメッセージキューを設定する方法|可視性タイムアウト・DLQ・WordPressの重い処理を非同期にする
Amazon SQS(Simple Queue Service)でメッセージキューを使うには、キューを作って「可視性タイムアウト」「ロングポーリング」「デッドレターキュー(DLQ)」の3つを用途に合わせて設定し、送る側はメッセージを入れるだけ、受け取る側(ワーカー)は取り出して処理し終えたら削除する、という流れを作ります。この記事では、WordPressで時間のかかる処理をリクエストから切り離す例を使って、キューの種類の選び方、設定値の意味、PHPからの送信、WP-CLIで動くワーカーの書き方、失敗したメッセージの扱いまでを説明します。
設定の上限値や既定値は見直されることがあるため、数値は公式ドキュメントの最新の記載(2026年10月現在)で確認してから決めてください。
なぜキューを挟むのか
WordPressで投稿を公開したときに外部サービスへデータを送る、画像を加工する、大量のメールを配る、といった処理をその場で実行すると、管理画面の応答が遅くなり、外部サービスが落ちていれば公開操作そのものが失敗します。
キューを挟むと、WordPressは「この処理をしてほしい」というメッセージをSQSに入れた時点で応答を返せます。実際の処理は別のワーカーが後から行い、失敗しても再試行できます。WP-Cronでも似たことはできますが、WP-Cronはサイトへのアクセスをきっかけに動くため、アクセスが少ないと処理が遅れ、複数台構成では同じ処理が重なることがあります。処理量が多い、失敗時に再試行させたい、複数台のサーバーで分担したい、といった場合にSQSが向いています。
スタンダードキューとFIFOキューの選び方
| 項目 | スタンダード | FIFO |
|---|---|---|
| 届く回数 | 少なくとも1回(まれに重複する) | 重複排除の仕組みがある |
| 順序 | 保証されない | メッセージグループ内で保証される |
| 名前 | 自由 | 末尾が .fifo |
| 向いている処理 | 順番に意味がない処理の大半 | 同じ注文の処理を順番どおりに行う、など |
迷ったらスタンダードを選び、ワーカー側で「同じメッセージを2回処理しても結果が変わらない」作りにしておくのが基本です。これを冪等(べきとう)性といいます。FIFOを選んでも、ワーカーが処理途中で落ちれば同じメッセージは再び取り出されるので、冪等に作る必要があることは変わりません。
キューを作る:3つの設定値の意味
可視性タイムアウト
ワーカーがメッセージを受け取ると、そのメッセージは一定時間ほかのワーカーから見えなくなります。この時間が可視性タイムアウトです。時間内に削除されなければ、処理に失敗したとみなされ、再び取り出せる状態に戻ります。
ここが短すぎると、処理中のメッセージが別のワーカーにも渡って二重処理になります。1件の処理にかかる最長時間より十分長くするのが目安です。処理時間がばらつく場合は、処理の途中で ChangeMessageVisibility を呼んで延長する方法もあります。
ロングポーリング
受信の待ち時間(ReceiveMessageWaitTimeSeconds)を最大の20秒にしておくと、メッセージが無いときにSQSが少し待ってから応答します。空の応答が減り、APIの呼び出し回数と費用を抑えられます。特に理由が無ければ20秒にしておきます。
デッドレターキュー(DLQ)
何度処理しても失敗するメッセージ(データが壊れている、対象の投稿が削除済み、など)は、そのままでは延々と再試行されます。受信回数が上限(maxReceiveCount)を超えたメッセージを別のキューに移す設定がDLQです。先にDLQを作り、そのARNを本体のキューに指定します。
aws sqs create-queue --queue-name wp-jobs-dlq --region ap-northeast-1
aws sqs create-queue --queue-name wp-jobs --region ap-northeast-1 \
--attributes file://wp-jobs-attributes.json
{
"VisibilityTimeout": "120",
"ReceiveMessageWaitTimeSeconds": "20",
"MessageRetentionPeriod": "1209600",
"RedrivePolicy": "{\"deadLetterTargetArn\":\"arn:aws:sqs:ap-northeast-1:123456789012:wp-jobs-dlq\",\"maxReceiveCount\":\"5\"}"
}
この例では、可視性タイムアウトを120秒、ロングポーリングを20秒、メッセージの保持期間を14日(1209600秒)、5回受信して削除されなければDLQへ移す、という設定にしています。RedrivePolicy の値はJSONを文字列にしたもので、内側の引用符をエスケープする必要がある点に注意してください。アカウントIDは例です。
DLQの保持期間は、本体のキューと同じかそれより長くしておきます。スタンダードキューでは、保持期間は最初に本体のキューに入った時刻から数えられるため、DLQのほうが短いと、移った直後に期限切れで消えることがあります。
WordPressからメッセージを送る
AWS SDK for PHPを使い、投稿が公開されたときにジョブを1件入れる例です。キューのURLは wp-config.php の定数で渡します。
<?php
/**
* Plugin Name: Queue jobs to SQS
*/
$wpdr_sdk = WP_CONTENT_DIR . '/mu-plugins/sqs-jobs/vendor/autoload.php';
if ( ! file_exists( $wpdr_sdk ) || ! defined( 'WPDR_SQS_QUEUE_URL' ) ) {
return;
}
require_once $wpdr_sdk;
function wpdr_sqs_client() {
return new Aws\Sqs\SqsClient( array(
'region' => 'ap-northeast-1',
'version' => 'latest',
) );
}
add_action( 'transition_post_status', function ( $new_status, $old_status, $post ) {
if ( 'publish' !== $new_status || 'publish' === $old_status || 'post' !== $post->post_type ) {
return;
}
try {
wpdr_sqs_client()->sendMessage( array(
'QueueUrl' => WPDR_SQS_QUEUE_URL,
'MessageBody' => wp_json_encode( array(
'type' => 'post_published',
'post_id' => (int) $post->ID,
) ),
) );
} catch ( Exception $e ) {
error_log( 'SQS send failed: ' . $e->getMessage() );
}
}, 10, 3 );
メッセージには投稿IDのような「処理対象を特定する最小限の情報」だけを入れ、本文や個人情報は入れません。ワーカーは受け取ったIDでデータベースから最新の内容を読み直します。こうするとメッセージのサイズ上限を気にせずに済み、キューに機密情報が残ることも避けられます。大きなデータを渡したいときは、データをS3に置いてそのキーだけを送るのが定石です。
WP-CLIでワーカーを動かす
ワーカーはWordPressの関数を使えると便利なので、WP-CLIのカスタムコマンドとして作ります。先ほどのmu-pluginの末尾に追加する例です。
if ( defined( 'WP_CLI' ) && WP_CLI ) {
WP_CLI::add_command( 'wpdr-queue work', function ( $args, $assoc_args ) {
$sqs = wpdr_sqs_client();
$max_loops = isset( $assoc_args['max-loops'] ) ? absint( $assoc_args['max-loops'] ) : 50;
for ( $i = 0; $i < $max_loops; $i++ ) {
$result = $sqs->receiveMessage( array(
'QueueUrl' => WPDR_SQS_QUEUE_URL,
'MaxNumberOfMessages' => 10,
'WaitTimeSeconds' => 20,
) );
$messages = $result->get( 'Messages' ) ?: array();
foreach ( $messages as $message ) {
$job = json_decode( $message['Body'], true );
try {
wpdr_handle_job( $job );
// 処理が成功したときだけ削除する
$sqs->deleteMessage( array(
'QueueUrl' => WPDR_SQS_QUEUE_URL,
'ReceiptHandle' => $message['ReceiptHandle'],
) );
} catch ( Exception $e ) {
// 削除しなければ可視性タイムアウト後に再試行され、上限を超えると DLQ へ移る
WP_CLI::warning( 'job failed: ' . $e->getMessage() );
}
}
}
} );
}
function wpdr_handle_job( $job ) {
if ( ! is_array( $job ) || empty( $job['type'] ) ) {
throw new Exception( 'invalid message' );
}
if ( 'post_published' === $job['type'] ) {
$post_id = absint( $job['post_id'] );
if ( get_post_meta( $post_id, '_wpdr_synced', true ) ) {
return; // 処理済みなら何もしない(重複して届いても安全にする)
}
// ここで外部サービスへの送信などを行う
update_post_meta( $post_id, '_wpdr_synced', time() );
}
}
要点は「処理が終わってから削除する」順番です。また、1回に最大10件を受け取る場合は、10件すべての処理が可視性タイムアウト内に終わる必要があります。間に合わない処理なら、受け取る件数を減らすか、タイムアウトを延ばします。先に削除してから処理すると、処理中にワーカーが落ちたときにメッセージが失われます。また、処理済みの印を投稿メタに残しておくことで、同じメッセージが2回届いても二重に処理しません。
ワーカーはsystemdなどで常駐させます。PHPを長時間動かし続けるとメモリが膨らむことがあるため、例では一定回数ループしたら終了し、systemdに再起動させる作りにしています。
# /etc/systemd/system/wp-queue-worker.service
[Unit]
Description=WordPress SQS worker
After=network-online.target
[Service]
User=www-data
WorkingDirectory=/var/www/html
ExecStart=/usr/local/bin/wp wpdr-queue work --max-loops=50
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
ユーザー名、パス、wp コマンドの場所は環境によって違うので合わせてください。sudo systemctl enable --now wp-queue-worker で起動し、journalctl -u wp-queue-worker -f でログを確認できます。
権限を分ける
送る側(Webサーバー)と受け取る側(ワーカー)では必要な権限が違います。EC2ならIAMロールで渡し、アクセスキーをコードに書かないようにします。
- 送る側:
sqs:SendMessage - 受け取る側:
sqs:ReceiveMessage、sqs:DeleteMessage、sqs:ChangeMessageVisibility、sqs:GetQueueAttributes
どちらもリソースを対象のキューのARNに絞ります。Webサーバーが乗っ取られた場合でも、キューのメッセージを読んだり消したりできない状態にしておけます。
動作の確認と運用
- キューの状態を見る:
aws sqs get-queue-attributes --queue-url <URL> --attribute-names ApproximateNumberOfMessages ApproximateNumberOfMessagesNotVisibleで、待っている件数と処理中の件数が分かります。待ち件数が増え続けるならワーカーが追いついていないか、止まっています。 - DLQを監視する:DLQの件数が0より大きくなったらCloudWatchアラームで通知するようにしておくと、失敗に早く気づけます。
- DLQから戻す:原因を直したら、コンソールのDLQ再処理(リドライブ)の機能で本体のキューへ戻せます。原因を直さずに戻すと、また同じように失敗してDLQに戻ってくるだけです。
キューの削除や設定変更は、処理待ちのメッセージを失う原因になります。本番のキューを触る前に、ステージング用に同じ設定のキューを作って試してください。
まとめ
- 迷ったらスタンダードキューを選び、ワーカーは同じメッセージを2回処理しても問題ない作りにする。
- 可視性タイムアウトは処理時間より長く、ロングポーリングは20秒、失敗が続くメッセージはDLQへ移す。
- メッセージには投稿IDなど最小限の情報だけを入れ、個人情報や大きなデータは入れない。
- ワーカーは処理に成功してからメッセージを削除する。WP-CLIのコマンドにしてsystemdで常駐させると扱いやすい。
- 送る側と受け取る側でIAMの権限を分け、DLQの件数を監視する。