スケーラブルなRabbitMQルーティングキーとバインディングを設計するためのベストプラクティス

予測可能で重複配信を回避し、コンシューマーに合わせてスケールするRabbitMQのルーティングキーとバインディングを設計します。

スケーラブルなRabbitMQルーティングキーとバインディングを設計するためのベストプラクティス

RabbitMQのルーティングキーとバインディングは追加は簡単ですが、後で解きほぐすのは困難です。すべてのサービスが独自のルーティングパターンを考案すると、重複配信、間違ったメッセージを受信するキュー、リスクを伴うトポロジ変更が発生する可能性があります。

最良の設計では、少数の予測可能なキー、狭いバインディング、および実際に必要な配信パターンに一致する交換タイプを使用します。

RabbitMQのルーティングとバインディングを理解する

ベストプラクティスに入る前に、基本的な概念を理解することが不可欠です。

  • 交換: プロデューサーからメッセージを受信し、ルーティングキーと交換タイプに基づいてキューにルーティングします。
  • キュー: アプリケーションによって消費されるまでメッセージを保存します。
  • バインディング: 交換とキューの間のリンクを作成します。メッセージが交換からキューにルーティングされる方法のルールを定義します。
  • ルーティングキー: プロデューサーがメッセージに含める文字列(多くの場合ドットで区切られます)。交換はルーティングキーを使用してメッセージの送信先を決定します。

異なる交換タイプ(Direct、Fanout、Topic、Headers)はルーティングキーの処理方法が異なり、バインディングの確立方法とメッセージの配信方法に影響を与えます。

スケーラブルなルーティングキーパターンの設計

ルーティングキーはメッセージを誘導するための主要なメカニズムです。適切に設計されたルーティングキー戦略は、スケーラビリティと効率にとって最も重要です。

1. 粒度の細かいルーティングにトピック交換を活用する

トピック交換は、パターンに基づいてメッセージをルーティングする必要がある複雑なルーティングシナリオに最適です。ワイルドカードマッチングメカニズムを使用します。

  • ワイルドカード: *(1つの単語に一致)と#(0個以上の単語に一致)。
  • パターン構造: 一般的なパターンはservice.event.detailです(例:user.created.v1order.paid.international)。

例:

topic交換がある場合、キューをorders.#にバインドできます。このキューは、orders.neworders.paid.internationalorders.shipped.domesticなど、orders.で始まるルーティングキーを持つすべてのメッセージを受信します。orders.paid.*にバインドされたキューは、orders.paid.internationalを受信しますが、orders.paidは受信しません。

2. ルーティングキーを一貫性があり予測可能に保つ

過度に複雑または一貫性のないルーティングキー形式を避けてください。予測可能な構造により、バインディングの管理とメッセージフローの理解が容易になります。

  • 規則を使用する: ルーティングキーに明確な命名規則を確立します(例:domain.action.resource.version)。
  • 過度な深さを避ける: 深くネストされたルーティングキーは扱いにくくなる可能性があります。可能であれば階層を簡素化することを検討してください。

3. あいまいさと重複するバインディングを最小限に抑える

トピック交換を使用する場合、ルーティングキーパターンがどのように重複する可能性があるかに注意してください。RabbitMQは、バインディングがルーティングキーに一致するすべてのキューにメッセージを配信します。

  • 具体性: 意図しない重複や欠落なしに、メッセージが目的のコンシューマーセットにルーティングされるようにパターンを設計します。
  • あいまいさの例: キューをlogs.#にバインドし、別のキューをlogs.error.*にバインドします。ルーティングキーlogs.error.databaseを持つメッセージは、両方のキューに配信されます。

4. キー以外のルーティングにHeaders交換を使用する

スケーラビリティではあまり一般的ではありませんが、Headers交換は、ルーティングの決定がルーティングキーだけでなくメッセージヘッダーに依存する場合に役立ちます。

  • ヘッダーマッチング: バインディングは特定のヘッダーのキーと値のペアに一致できます。
  • ユースケース: 事前定義されたキー構造よりもメタデータがルーティングに関連する場合に役立ちますが、マッチングにはより多くのリソースを消費する可能性があります。

バインディング設定の最適化

バインディングは交換をキューに接続する接着剤です。その設定はパフォーマンスとリソース使用率に直接影響します。

1. 不要なバインディングとキューを避ける

各バインディングとキューはリソースを消費します。トポロジを定期的に監査して、未使用または冗長なエンティティを削除します。

  • 動的な作成/削除: アプリケーションが動的にバインディングを作成する場合は、不要になったときにそれらをクリーンアップするようにしてください。
  • コンシューマー数: 単一のキューに複数のコンシューマーを持たせることができます。可能であれば、同じコンシューマータイプのインスタンスごとに個別のキューを作成しないでください。

2. 正確な1対1ルーティングにDirect交換を使用する

メッセージを正確なルーティングキーの一致に基づいて特定のキューに送信する必要があるシナリオでは、Direct交換はトピック交換よりも効率的です。

  • 完全一致: ルーティングキーXを持つメッセージは、Direct交換でルーティングキーXでバインドされたキューにのみ配信されます。
  • シンプルさ: 単純なプロデューサー-コンシューマーパターンに最適です。

3. ブロードキャストにFanout交換を使用する

ルーティングキーに関係なく、メッセージを特定のイベントにサブスクライブしているすべてのキューに送信する必要がある場合、Fanout交換が最も効率的です。

  • ルーティングキーを無視: ルーティングキーは無視されます。メッセージはバインドされたすべてのキューにファンアウトされます。
  • 高スループット: 通知や更新のブロードキャストに最適です。

4. デッドレター交換(DLX)を戦略的に実装する

デッドレター交換は、配信できない、または拒否されたメッセージを処理するために不可欠です。適切な設定により、メッセージの損失を防ぎ、デバッグに役立ちます。

  • 設定: キューにx-dead-letter-exchangeを設定し、元のルーティングキーを上書きする場合にのみx-dead-letter-routing-keyを設定します。
  • 目的: 未処理または拒否されたメッセージはDLXにルーティングされ、多くの場合、検査用の専用キューに送られます。

例:

キューprocessing_queueには、処理できないメッセージをルーティングキーunprocesseddlx.unprocessedにルーティングするDLXが設定されている場合があります。これにより、失敗したメッセージを監視して再処理できます。

# DLX引数を使用したキュー宣言の例
queues:
  processing_queue:
    durable: true
    arguments:
      x-dead-letter-exchange: dlx.unprocessed
      x-dead-letter-routing-key: unprocessed

5. キューの長さとメッセージレートを監視する

定期的な監視は、ルーティングまたはバインディングの問題によって引き起こされる潜在的なボトルネックを特定するための鍵です。

  • ツール: RabbitMQの管理UI、Prometheus/Grafana、またはその他の監視ソリューションを使用します。
  • 監視するメトリクス: キューの深さ、メッセージレート(入出力)、コンシューマー使用率、未確認メッセージ。
  • アクション: キューが急速に増加している場合、またはメッセージレートが予期せず低下している場合は、関連するルーティングキーとバインディングを調査します。

スケーラビリティのための高度な考慮事項

1. ルーティングキーによるパーティショニングとシャーディング

非常に高いスループットのシナリオでは、ルーティングキーを使用してデータを複数のキューとコンシューマーに分割する場合があります。これには、ルーティングキー自体が負荷分散に役立つ戦略が含まれます。

  • 例: user.events.user123のようなルーティングキーを使用できます。コンシューマーサービスは、ユーザーのサブセットのイベントのみを処理するように設計されている場合や、特定のユーザーID範囲にバインドされた複数のキューがある場合があります。
  • 複雑さ: これにより、アプリケーションロジックとRabbitMQトポロジ管理にかなりの複雑さが追加されます。

2. フェデレーションとShovelプラグイン

複数のRabbitMQクラスターまたは地理的に分散したシステムを扱う場合、フェデレーションとShovelプラグインはそれらの間のルーティングを管理するのに役立ちます。ルーティングキーの設計に直接関係するわけではありませんが、メッセージが異なる環境間で目的の宛先に確実に到達するように、明確に定義されたルーティングパターンに依存します。

3. プロデューサー側のフィルタリング(注意して使用)

RabbitMQはルーティング用に設計されていますが、すべてを送信して交換/キュー レベルでフィルタリングするよりも、送信する必要があるメッセージのみを生成する方が効率的な場合があります。これにより、フィルタリングロジックがプロデューサーに移行します。

  • トレードオフ: RabbitMQの負荷を軽減しますが、プロデューサーのロジックが複雑になり、動的なルーティング変更が難しくなる可能性があります。

まとめ

優れたRabbitMQルーティングは、読んでいて退屈であるべきです。コンシューマーがパターンを必要とする場合はトピック交換を、完全一致で十分な場合はダイレクト交換を、バインドされたすべてのキューがメッセージを受信する必要がある場合はファンアウト交換を使用します。サービス変更時にバインディングを確認し、デッドレターパスを可視化し、すべてのワイルドカードを再確認する価値のあるものとして扱います。