web-dev-qa-db-ja.com

RabbitMQ:トピック交換を伴う永続メッセージ

私はRabbitMQを初めて使用します。

「トピック」交換を設定しました。コンシューマーは、パブリッシャーの後に開始される場合があります。消費者は、起動する前に送信されたメッセージで、まだ消費されていないメッセージを受信できるようにしたいと思います。

交換は、次のパラメーターでセットアップされます。

exchange_type => 'topic'
durable => 1
auto_delete => 0
passive => 0

メッセージは次のパラメーターで公開されます。

delivery_mode => 2

消費者はget()を使用して、交換からメッセージを取得します。

残念ながら、クライアントが起動する前に発行されたメッセージは失われます。さまざまな組み合わせを使用しました。

私の問題は、交換がメッセージを保持していないことだと思います。パブリッシャーとキューの間にキューが必要な場合があります。しかし、これは、メッセージがキーによってルーティングされる「トピック」交換では機能しないようです。

私はどのように進むべきか考えています。私はPerlバインディングNet :: RabbitMQ(問題ではない)とRabbitMQ 2.2.0を使用しています。

62
Julien

パブリッシュ時にメッセージを処理できる接続されたコンシューマーがない場合、メッセージを保存するために永続キューが必要です。

交換はメッセージを保存しませんが、キューは保存できます。紛らわしい部分は、交換は「耐久性がある」とマークできることですが、実際に意味するのは、交換自体がブローカーを再起動してもそこにあるということです、しかしnotは、その交換に送信されたメッセージが自動的に永続化されることを意味します。

そのため、次の2つのオプションがあります。

  1. パブリッシャーを開始する前に管理手順を実行して、キューを作成します。これを行うには、Web UIまたはコマンドラインツールを使用できます。アクティブなコンシューマがない場合でも、ルーティングされるメッセージが保存されるように、永続キューとして作成してください。
  2. 消費者は、起動時に常に交換とキューを宣言する(したがって自動作成する)ようにコーディングされている(そして、それらを永続として宣言する)と仮定すると、ちょうどすべての消費者を少なくとも1回実行するパブリッシャーを開始する前に。これにより、すべてのキューが正しく作成されます。その後、実際に必要になるまでコンシューマをシャットダウンできます。これは、キューにルーティングされる将来のメッセージがキューに永続的に保存されるためです。

#1に行きます。実行する手順はそれほど多くない場合があり、必要な手順をスクリプト化して、繰り返し実行できるようにすることができます。さらに、すべての消費者が(それぞれ専用のキューを持つのではなく)同じ単一のキューからプルしようとする場合、実際には最小限の管理オーバーヘッドです。

キューは、適切に管理および制御されるものです。そうしないと、不正なコンシューマーが永続キューを宣言し、それらを数分間使用しても、再び使用することはできません。すぐに、サイズを縮小することなく永続的に成長するキューがあり、ブローカーの黙示録が差し迫っています。

65
Brian Kelly

ブライアンが述べたように、交換機はメッセージを保存せず、主に別の交換機またはキューにメッセージをルーティングします。交換がキューにバインドされていない場合、その交換に送信されるすべてのメッセージは「失われます」

パブリッシャスクリプトで固定クライアントキューを宣言する必要はありません。これはスケーラブルではない可能性があるためです。パブリッシャーがキューを動的に作成し、エクスチェンジツーエクスチェンジバインディングを使用して内部的にルーティングできます。

RabbitMQは、トポロジの柔軟性、デカップリング、およびその他の利点を可能にする交換間バインディングをサポートしています。詳細については、 RabbitMQ Exchange to Exchange Bindings [AMPQ] をご覧ください。

RabbitMQ ExchangeからExchangeへのバインディング

Example Topology

例Pythonキューを使用しているコンシューマが存在しない場合に永続性を備えた交換間バインディングを作成するコード。

#!/usr/bin/env python
import pika
import sys


connection = pika.BlockingConnection(pika.ConnectionParameters(
Host='localhost'))
channel = connection.channel()


#Declares the entry exchange to be used by all producers to send messages. Could be external producers as well
channel.exchange_declare(exchange='data_gateway',
exchange_type='fanout',
durable=True,
auto_delete=False)

#Declares the processing exchange to be used.Routes messages to various queues. For internal use only
channel.exchange_declare(exchange='data_distributor',
exchange_type='topic',
durable=True,
auto_delete=False)

#Binds the external/producer facing exchange to the internal exchange
channel.exchange_bind(destination='data_distributor',source='data_gateway')

##Create Durable Queues binded to the data_distributor exchange
channel.queue_declare(queue='trade_db',durable=True)
channel.queue_declare(queue='trade_stream_service',durable=True)
channel.queue_declare(queue='ticker_db',durable=True)
channel.queue_declare(queue='ticker_stream_service',durable=True)
channel.queue_declare(queue='orderbook_db',durable=True)
channel.queue_declare(queue='orderbook_stream_service',durable=True)

#Bind queues to exchanges and correct routing key. Allows for messages to be saved when no consumer is present
channel.queue_bind(queue='orderbook_db',exchange='data_distributor',routing_key='*.*.orderbook')
channel.queue_bind(queue='orderbook_stream_service',exchange='data_distributor',routing_key='*.*.orderbook')
channel.queue_bind(queue='ticker_db',exchange='data_distributor',routing_key='*.*.ticker')
channel.queue_bind(queue='ticker_stream_service',exchange='data_distributor',routing_key='*.*.ticker')
channel.queue_bind(queue='trade_db',exchange='data_distributor',routing_key='*.*.trade')
channel.queue_bind(queue='trade_stream_service',exchange='data_distributor',routing_key='*.*.trade')
19
Skillachie