EN

Pub/SubとQueueの違いを理解する

はじめに

最近は、フレームワークやミドルウェアといった「動かすための技術」そのものよりも、ドメインの複雑さに応じてアーキテクチャをどう選ぶかに興味が移ってきています。ドメインがシンプルなうちは1つのアプリケーションで素直に書けばよく、関係者や業務が増えて複雑になってくると、サービス同士をどうつなぐかが設計の中心になってきます。

そのつなぎ方の代表が非同期メッセージングで、イベント駆動アーキテクチャを組むときの土台にもなります。よく登場するのが Queue(メッセージキュー) と Pub/Sub(Publish/Subscribe) です。両者はProducerとConsumerを疎結合にする点では共通していますが、メッセージを「誰に」「いくつ」届けるかという目的が異なります。

端的にいえば、Queueは仕事を複数のWorkerで分担するための仕組み、Pub/Subは発生した出来事を複数の関係者へ知らせるための仕組みです。Azure Service Busの公式ドキュメントでも、QueueはPoint-to-Point、TopicとSubscriptionは1対多のPublish/Subscribeとして整理されています。


この記事の内容は次のとおりです。

Queueとは

Queueは、Producerが投入したメッセージを一時的に保持し、Consumerが取り出して処理する仕組みです。複数のConsumerが同じQueueを監視している場合でも、通常は1つのメッセージをそのうち1つのConsumerだけが処理します。

Producer
   │
   ▼
 Queue ──┬──▶ Worker A
         ├──▶ Worker B
         └──▶ Worker C

1回の配信につき、いずれか1つのWorkerが処理する

この形式は Competing Consumersパターン とも呼ばれます。Workerを増やすと、同じ処理を重複して実行するのではなく、Queueに溜まった仕事を分担して処理できます。

Queueが適している代表例は次のとおりです。

  • 画像や動画の変換
  • メール送信
  • PDF生成
  • バッチ処理
  • 外部API連携
  • 突発的なアクセスを吸収するためのバッファ

AWSの公式比較でも、Amazon SQSはWork Queue、バッチ処理、イベントのバッファリングなどに適したサービスと説明されています。

Pub/Subとは

Pub/Subでは、Publisherがメッセージを直接Consumerへ送るのではなく、Topicへ発行します。それぞれのSubscriberはTopicに紐づくSubscriptionを通してメッセージを受信します。

                 ┌─▶ Subscription A ─▶ 在庫サービス
Publisher ─▶ Topic
                 ├─▶ Subscription B ─▶ メールサービス
                 └─▶ Subscription C ─▶ 分析サービス

同じイベントが、それぞれのSubscriptionへ配信される

Topicに複数のSubscriptionがある場合、発行されたメッセージは各Subscriptionへコピーされます。PublisherはSubscriberの数や具体的な受信者を知る必要がないため、送信側と受信側を疎結合にできます(Google Cloud Pub/Subのドキュメント)。

たとえば注文サービスが OrderCreated イベントを発行した場合、在庫サービスは在庫を減らし、メールサービスは注文確認メールを送り、分析サービスは売上情報を記録できます。新しい処理が必要になったときは、新しいSubscriptionとConsumerを追加することで、Publisherを変更せずに機能を拡張できます。

違いの整理

観点QueuePub/Sub
通信モデルPoint-to-PointPublish/Subscribe
主な関係1対11対多
1メッセージの受信者複数Consumerのうち通常1つ各Subscriptionがそれぞれ受信
Consumerを増やす目的負荷分散・並列処理新しい処理・関係者への配信
主な用途ジョブやタスクの実行イベントの通知・ファンアウト
代表例画像変換、メール送信、バッチ処理注文作成、会員登録、商品更新の通知

重要なのは、QueueはCommandやTaskとの相性がよく、Pub/SubはEventとの相性がよいという点です。「画像を変換せよ」のように実行してほしい処理を表すならQueueが自然です。一方、「注文が作成された」のように、すでに起きた事実を複数サービスへ通知するならPub/Subが自然です。

組み合わせる構成

Pub/SubとQueueは排他的な選択肢ではなく、実際のシステムでは組み合わせて利用されます。Topicの各Subscriptionは受信側から見ると仮想的なQueueとして振る舞い、Subscription間では同じイベントを共有し、同一Subscription内では複数Consumerが処理を分担できます。

Topic
  ├─▶ Subscription A
  │      ├─▶ Worker A1
  │      └─▶ Worker A2
  │
  └─▶ Subscription B
         ├─▶ Worker B1
         └─▶ Worker B2

この構成では、イベントはSubscription AとBの両方に届きます。ただし、Subscription A内ではA1とA2のどちらかが各メッセージを処理します。つまり、Subscription間ではPub/Subによるファンアウト、Subscription内ではQueue的な負荷分散が行われます。Google Cloud Pub/Subでも、Topicはメッセージを各Subscriptionへ配信し、同一Subscriptionに複数Subscriberがいる場合は、そのうち1つだけが各メッセージを受信します。

AWSでの構成例

AWSでは、Amazon SNSをPub/SubのTopic、Amazon SQSをQueueとして組み合わせる構成が代表的です。

SNS Topic
  ├─▶ SQS Queue A ─▶ 在庫サービスのWorker群
  ├─▶ SQS Queue B ─▶ メールサービスのWorker群
  └─▶ SQS Queue C ─▶ 分析サービスのWorker群

SNS Topicに発行されたメッセージは、購読している各SQS Queueへファンアウトされます。それぞれのサービスは専用Queueから自分のペースでメッセージを処理でき、障害や処理速度の違いをサービス間で分離できます。AWSの公式FAQでも、複数のSQS QueueをSNS TopicにSubscribeすることで、同一メッセージをすべてのQueueへ配信できると説明されています。

設計上の注意点

QueueやPub/Subを導入しても、メッセージが必ず1回だけ処理されるとは限りません。サービスや設定によっては同じメッセージが再配信されるため、Consumer側では同一メッセージを複数回処理しても結果が壊れない冪等性を考慮する必要があります。Google Cloud Pub/Subはデフォルトでat-least-once配信であり、Acknowledgementされなかったメッセージを再配信します。

また、処理に失敗したメッセージを無限に再試行すると、正常な処理を圧迫する可能性があります。そのため、再試行回数、指数バックオフ、Dead Letter Queue、監視とアラートを設計します。メッセージ順序が必要な場合も、QueueやTopic全体で暗黙に保証されると考えず、利用するサービスの順序保証とパーティション単位を確認することが重要です。

選び方

次の問いで判断すると整理しやすくなります。

  • 「誰でもよいので、この仕事を1回処理してほしい」ならQueue
  • 「この出来事を複数のサービスへ知らせたい」ならPub/Sub
  • 「複数サービスへ知らせ、各サービス内でも処理を分担したい」ならPub/SubとQueueの組み合わせ

Queueは処理の分担、Pub/Subは情報の共有を中心に考えると、両者の役割を混同しにくくなります。ただし、製品によってはSubscription自体がQueueのように動作するため、概念的なパターンとクラウドサービスの商品名を分けて理解することが大切です。

参考