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


144

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、4、6が表示されます

私の質問は:

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

  • これは一般的に行われていますか?代わりに、単一のコンシューマを使用して、エクスチェンジにメッセージを2つの個別のキューにルーティングさせるだけでよいですか


5
私はRabbitMQのエキスパートではありません。ただし、現在持っているものはキューと呼ばれますが、必要なのはトピックです。このチュートリアルを参照してください:rabbitmq.com/tutorials/tutorial-five-python.htmlキューとトピックの詳細:msdn.microsoft.com/en-us /library/windowsazure/hh367516.aspx
UrbanEsc

1
私は彼が実際にファンアウトを望んでいると思いますが、トピックは同様に機能し、後でより多くの制御を提供します。
robthewolf

@UrbanEscに感謝します。トピックは、1つのメッセージを複数のキューにヒットさせることで問題を解決するようであり、したがって、各キューのコンシューマーによって消費されます。これは、私の特定のケースの複数のキュー/単一の消費者シナリオにさらに傾いています。
mikemaccana

1
2018年の場合(および2016年以前でも)、答えはIMOのカフカのようなものを使用することです。
WattsInABox

回答:


115

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

いいえ、コンシューマが同じキューにいる場合は違います。RabbitMQのAMQP概念ガイドから:

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

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

これは一般的に行われていますか?代わりに、単一のコンシューマを使用して、エクスチェンジにメッセージを2つの個別のキューにルーティングさせるだけでよいですか

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

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

ここに私の送信ファンアウト交換があります:

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) 
 })
})

1
ファンアウトは明らかにあなたが望んだものでした。ここでは役に立たないでしょうが、キュー内でのラウンドロビンの動作は構成可能であることを述べておきます。 int prefetchCount = 1; channel.basicQos(prefetchCount); これにより、各コンシューマは、前のメッセージが終了するとすぐにメッセージを受信できます。メッセージを交互に受信する代わりに。繰り返しますが、問題は解決しませんが、人々が知るのに役立つ可能性があります。こちらの例http://www.rabbitmq.com/tutorials/tutorial-two-java.html下のFair Dispatch
Ommit

3
明確にするために:「デフォルトの交換」はnode-amqp固有ではありません。これは、次のルールを持つ一般的なAMQPの概念です。デフォルトの交換にパブリッシュされたメッセージは、AMQPブローカーによってキュー名として扱われる(そのメッセージがパブリッシュされる)ルーティングキーです。したがって、直接キューに公開できるようです。しかし、あなたは違います。ブローカーは、キュー名と同じルーティングキーを使用して、各キューをデフォルトの交換にバインドするだけです。
Ruslan Stelmachenko

2
rabbitmqのApache activemq jmsトピックに代わるものはありますか?
pantonis

同じユーザーが複数のデバイスからログインした場合、メッセージは1つのデバイスしか取得しません。どうすれば解決できますか?
Rafiq 2017年

@Rafiqあなたはこれについて質問すべきです。
mikemaccana 2017年

27

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


25

最後の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データを取得します。

これは、公開アプリが受信アプリを知らないか気にせずにデータを送信するための理想的な方法です。


8

はい、各コンシューマは同じメッセージを受信できます。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でそれを行う方法を見つけるのは良いことです。交換機に接続されているすべてのキューにメッセージを送信する単純なファンアウト(チュートリアル3)を実行したいようです。

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

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


「これは一般的に行われていますか」を除いて、素晴らしい答えです。私は「各コンシューマーが同じメッセージを受信すること」に言及していました-これは一般的に行われていません(同じキューにいるコンシューマーは常にラウンドロビンで)。おそらく、十分に明確にされていないことに対する私の責任です。
mikemaccana

実は、それはあなたがそれを何に使いたいかによります。pub / subまたはワークキューの2つの基本的な選択肢があります。最初の設定はワークキューでしたが、ファンアウトパブ/サブが必要でした。彼らの指摘は、ここでの一般的な使い方は、あなたが何をしたいかに完全に依存しているということです。
robthewolf

もちろん、ワークキューでは、同じメッセージ(たとえば、同じメッセージID)は異なるコンシューマによって処理されません-暗黙的にラウンドロビンです。繰り返しますが、これはおそらく十分に明確にされていないことに対する私の責任です。
mikemaccana

ここでは、クロスの目的で話しているように見えます。
robthewolf

混乱について申し訳ありません。同じキューのコンシューマーが同じメッセージIDを処理するワークキューを作成する方法がある場合は、参照してください。そうでなければ、私は他の場所で読んだことを信じ続けます。
mikemaccana


3

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);
            }
        });
    });

1

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


1

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

  • メッセージのキューがあります(メッセージを受信するためのソースです。q111という名前を付けてください)

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

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

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

  • 動的rabbitmq-shovelを使用して、目的のキュー(q111)からメッセージをピックアップし、ファンアウト交換(この目的専用に作成および専用の交換)に公開します。
  • 次に、コンシューマーA、B、C(queue(q111)をリッスンしていた)を再構成して、各コンシューマーの排他的で匿名のキューを使用して、このファンアウト交換から直接リッスンします。

注:この概念を使用している間は、ソースキュー(q111)から直接消費しないでください。すでに消費されているメッセージは、ファンアウトエクスチェンジにシャベル送信されないためです。

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



0

ファンアウトエクスチェンジャーを使用してメッセージの送信を確認する必要があると思います。このようにして、RabbitMQは異なるコンシューマに対して同じメッセージを受信します。RabbitMQは、この新しいコンシューマ/サブスクライバごとに異なるキューを作成します。

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


-1

このシナリオには、ここでの回答では見つけられない興味深いオプションが1つあります。

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

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

そして、ループに注意してください(すべてのコンシューマーがメッセージをnack + requeueする場合)!


1
これは決して拡大縮小されないので、私はこれに対して強く助言します。コンシューマーの順序はありません。メッセージをリキューしないコンシューマーBが、メッセージを処理してリキューする前にメッセージを受信することを保証できません。前述のループは問題です。「これは一般的に正しい方法ではない」とあなたが言うように、私はこれが他の答えよりも良いシナリオを考えることはできません。
Kevin Streicher
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.