web-dev-qa-db-ja.com

RabbitMQ / AMQP:単一のキュー、同じメッセージの複数のコンシューマー?

私は一般的にRabbitMQとAMQPを使用し始めたところです。

  • メッセージのキューがあります
  • 複数のコンシューマーがありますが、同じメッセージを使用して異なることをしたいと思います。

RabbitMQのドキュメントのほとんどは、ラウンドロビンに焦点を当てているようです。つまり、単一のメッセージが単一のコンシューマーによって消費され、負荷が各コンシューマーに分散しているようです。これは実際に私が目撃した行動です。

例:プロデューサーには単一のキューがあり、2秒ごとにメッセージを送信します。

var amqp = require('amqp');
var connection = amqp.createConnection({ Host: "localhost", port: 5672 });
var count = 1;

connection.on('ready', function () {
  var sendMessage = function(connection, queue_name, payload) {
    var encoded_payload = JSON.stringify(payload);  
    connection.publish(queue_name, encoded_payload);
  }

  setInterval( function() {    
    var test_message = 'TEST '+count
    sendMessage(connection, "my_queue_name", test_message)  
    count += 1;
  }, 2000) 


})

そして、ここに消費者がいます:

var amqp = require('amqp');
var connection = amqp.createConnection({ Host: "localhost", port: 5672 });
connection.on('ready', function () {
  connection.queue("my_queue_name", function(queue){
    queue.bind('#'); 
    queue.subscribe(function (message) {
      var encoded_payload = unescape(message.data)
      var payload = JSON.parse(encoded_payload)
      console.log('Recieved a message:')
      console.log(payload)
    })
  })
})

コンシューマを2回起動すると、各コンシューマがラウンドロビン動作で代替メッセージを消費していることがわかります。たとえば、1つの端末にメッセージ1、3、5、2つの端末に2、4、6が表示されます。その他

私の質問は:

  • 各消費者に同じメッセージを受信させることはできますか?すなわち、両方の消費者がメッセージ1、2、3、4、5、6を受け取りますか?これはAMQP/RabbitMQで何と呼ばれますか?通常はどのように構成されていますか?

  • これは一般的に行われていますか?代わりに、単一のコンシューマを使用して、メッセージを2つの個別のキューにルーティングするだけで交換する必要がありますか?

114
mikemaccana

各消費者に同じメッセージを受信させることはできますか?すなわち、両方の消費者がメッセージ1、2、3、4、5、6を受け取りますか?これはAMQP/RabbitMQで何と呼ばれますか?通常はどのように構成されていますか?

いいえ、消費者が同じキューにいる場合ではありません。 RabbitMQの AMQP Concepts ガイドから:

aMQP 0-9-1では、メッセージはコンシューマ間で負荷分散されることを理解することが重要です。

これは、キュー内のラウンドロビン動作が指定されたであり、構成できないことを意味するようです。つまり、同じメッセージIDを複数のコンシューマーで処理するには、個別のキューが必要です。

これは一般的に行われていますか?代わりに、単一のコンシューマを使用して、メッセージを2つの個別のキューにルーティングするだけで交換する必要がありますか?

いいえ、そうではありません。それぞれのコンシューマが同じメッセージIDを処理する単一のキュー/複数のコンシューマは不可能です。交換によってメッセージを2つの別々のキューにルーティングする方が確かに優れています。

あまり複雑なルーティングは必要ないので、fanout exchangeがこれをうまく処理します。 node-amqpには、メッセージを接続に直接公開できる「デフォルトの交換」という概念がありますが、ほとんどのAMQPメッセージは特定の交換に公開されるため、以前のExchangeにはあまり重点を置きませんでした。

送信と受信の両方の私のファンアウト交換は次のとおりです。

var amqp = require('amqp');
var connection = amqp.createConnection({ Host: "localhost", port: 5672 });
var count = 1;

connection.on('ready', function () {
  connection.exchange("my_exchange", options={type:'fanout'}, function(exchange) {   

    var sendMessage = function(exchange, payload) {
      console.log('about to publish')
      var encoded_payload = JSON.stringify(payload);
      exchange.publish('', encoded_payload, {})
    }

    // Recieve messages
    connection.queue("my_queue_name", function(queue){
      console.log('Created queue')
      queue.bind(exchange, ''); 
      queue.subscribe(function (message) {
        console.log('subscribed to queue')
        var encoded_payload = unescape(message.data)
        var payload = JSON.parse(encoded_payload)
        console.log('Recieved a message:')
        console.log(payload)
      })
    })

    setInterval( function() {    
      var test_message = 'TEST '+count
      sendMessage(exchange, test_message)  
      count += 1;
    }, 2000) 
 })
})
95
mikemaccana

rabbitmqチュートリアル を読んでください。メッセージはキューではなく交換するために公開します。その後、適切なキューにルーティングされます。あなたの場合、各コンシューマーに個別のキューをバインドする必要があります。そうすれば、彼らは完全に独立してメッセージを消費することができます。

20
driushkin

最後の2つの答えはほぼ正しいです。メッセージを生成するアプリがたくさんあるので、最終的には異なる消費者になる必要があるため、プロセスは非常に簡単です。

同じメッセージに複数のコンシューマーが必要な場合は、次の手順を実行します。

メッセージを受信するアプリごとに1つずつ、複数のキューを作成し、各キュープロパティで、amq.direct交換でルーティングタグを「バインド」します。公開アプリを変更してamq.directに送信し、ルーティングタグ(キューではなく)を使用します。 AMQPは、同じバインディングで各キューにメッセージをコピーします。魅力のように動作します:)

例:生成するJSON文字列があるとしましょう。ルーティングタグ「new-sales-order」を使用して「amq.direct」エクスチェンジに公開します。注文を印刷するorder_printerアプリのキューがあり、注文のコピーを送信してクライアントに請求する請求システムのキュー。履歴/コンプライアンスの理由で注文をアーカイブするWebアーカイブシステムがあり、他の情報が入ってくると注文が追跡されるクライアントWebインターフェイスがあります。オーダー。

したがって、私のキューは次のとおりです。order_printer、order_billing、order_archive、order_trackingすべてにバインディングタグ「new-sales-order」がバインドされており、4つすべてがJSONデータを取得します。

これは、パブリッシングアプリが受信アプリを認識したり気にしたりすることなくデータを送信するための理想的な方法です。

15
z900collector

はい、各消費者は同じメッセージを受信できます。 http://www.rabbitmq.com/tutorials/tutorial-three-python.html http://www.rabbitmq.com/tutorials/tutorial-four-python.htmlをご覧ください- http://www.rabbitmq.com/tutorials/tutorial-five-python.html

メッセージをルーティングするさまざまな方法。 pythonとJavaに適していることは知っていますが、原則を理解し、あなたが何をしているのかを判断し、JSでそれを行う方法を見つけるのは良いことです。単純なファンアウト( チュートリアル )を行いたいように聞こえます。これは、交換に接続されたすべてのキューにメッセージを送信します。

あなたがしていることとやりたいこととの違いは、基本的にあなたが設定し、交換またはファンアウトを入力しようとしていることです。ファンアウトの例外は、接続されているすべてのキューにすべてのメッセージを送信します。各キューには、すべてのメッセージに個別にアクセスできるコンシューマーがあります。

はい、これは一般的に行われています。AMPQの機能の1つです。

6
robthewolf

送信パターンは1対1の関係です。複数の受信者に「送信」したい場合は、pub/subパターンを使用する必要があります。詳細については、 http://www.rabbitmq.com/tutorials/tutorial-three-python.html を参照してください。

6
Peter Ritchie

RabbitMQ/AMQP:単一のキュー、同じメッセージとページの更新のための複数のコンシューマー。

rabbit.on('ready', function () {    });
    sockjs_chat.on('connection', function (conn) {

        conn.on('data', function (message) {
            try {
                var obj = JSON.parse(message.replace(/\r/g, '').replace(/\n/g, ''));

                if (obj.header == "register") {

                    // Connect to RabbitMQ
                    try {
                        conn.exchange = rabbit.exchange(exchange, { type: 'topic',
                            autoDelete: false,
                            durable: false,
                            exclusive: false,
                            confirm: true
                        });

                        conn.q = rabbit.queue('my-queue-'+obj.agentID, {
                            durable: false,
                            autoDelete: false,
                            exclusive: false
                        }, function () {
                            conn.channel = 'my-queue-'+obj.agentID;
                            conn.q.bind(conn.exchange, conn.channel);

                            conn.q.subscribe(function (message) {
                                console.log("[MSG] ---> " + JSON.stringify(message));
                                conn.write(JSON.stringify(message) + "\n");
                            }).addCallback(function(ok) {
                                ctag[conn.channel] = ok.consumerTag; });
                        });
                    } catch (err) {
                        console.log("Could not create connection to RabbitMQ. \nStack trace -->" + err.stack);
                    }

                } else if (obj.header == "typing") {

                    var reply = {
                        type: 'chatMsg',
                        msg: utils.escp(obj.msga),
                        visitorNick: obj.channel,
                        customField1: '',
                        time: utils.getDateTime(),
                        channel: obj.channel
                    };

                    conn.exchange.publish('my-queue-'+obj.agentID, reply);
                }

            } catch (err) {
                console.log("ERROR ----> " + err.stack);
            }
        });

        // When the visitor closes or reloads a page we need to unbind from RabbitMQ?
        conn.on('close', function () {
            try {

                // Close the socket
                conn.close();

                // Close RabbitMQ           
               conn.q.unsubscribe(ctag[conn.channel]);

            } catch (er) {
                console.log(":::::::: EXCEPTION SOCKJS (ON-CLOSE) ::::::::>>>>>>> " + er.stack);
            }
        });
    });
3
durai

このシナリオには興味深い選択肢が1つありますが、ここの答えにはありません。

1つのコンシューマで「リキュー」機能を使用してメッセージをNackして、別のコンシューマで処理できます。一般的に言って、それは正しい方法ではありませんが、誰かにとっては十分かもしれません。

https://www.rabbitmq.com/nack.html

また、ループに注意してください(すべての消費者がメッセージをnack + requeueする場合)!

1
Alexus1024

私があなたのケースを評価するとき:

  • メッセージのキューがあります(メッセージを受信するためのソース、名前をq111にします)

  • 複数の消費者がいますが、同じメッセージで異なることをしたいと思います。

ここでの問題は、このキューで3つのメッセージが受信され、メッセージ1がコンシューマAによって消費され、他のコンシューマBおよびCがメッセージ2および3を消費することです。接続されている3つのコンシューマ(A、B、C)すべてに対するこれら3つのメッセージ(1、2、3)をすべて同時に。

これを実現するために多くの構成を行うことができますが、簡単な方法は次の2つのステップの概念を使用することです。

  • 動的rabbitmq-shovelを使用して、目的のキュー(q111)からメッセージをピックアップし、ファンアウトエクスチェンジ(エクスチェンジはこの目的のために専用に作成されたエクスチェンジ)に発行します。
  • 次に、消費者A、BおよびC(キュー(q111)をリッスンしていた)を再構成して、各消費者専用の匿名キューを使用してこのFanout交換から直接リッスンします。

注:この概念を使用している間は、ソースキュー(q111)から直接消費しないでください。既に消費されたメッセージはFanout交換にシャベルされません。

これがあなたの正確な要件を満たしていないと思うなら...あなたの提案を投稿してください:-)

1

必要な動作を取得するには、各コンシューマーが独自のキューから消費するようにします。すべてのキューにメッセージを一度に取得するには、非直接交換タイプ(トピック、ヘッダー、ファンアウト)を使用する必要があります。

1
Skylos

fan-out交換機を使用してメッセージの送信を確認する必要があると思います。そうすれば、テーブルRabbitMQの下で、この新しいコンシューマー/サブスクライバーごとに異なるキューを作成し、異なるコンシューマーに対して同じメッセージを受信します。

これは、javascriptのチュートリアル例を参照するためのリンクです https://www.rabbitmq.com/tutorials/tutorial-one-javascript.html

0

私が amqplib ライブラリを使用している場合、 便利な例Publish/Subscribe RabbitMQチュートリアル の実装があります便利かもしれません。

0
brettjonesdev