web-dev-qa-db-fra.com

RabbitMQ / AMQP: file d'attente unique, plusieurs consommateurs pour le même message?

Je commence tout juste à utiliser RabbitMQ et AMQP en général.

  • J'ai une file de messages
  • J'ai plusieurs consommateurs, ce que je voudrais faire différentes choses avec le même message.

La majeure partie de la documentation RabbitMQ semble être axée sur le va-et-vient, c'est-à-dire lorsqu'un seul message est consommé par un seul consommateur, la charge étant répartie entre chaque consommateur. C’est bien le comportement dont je suis témoin.

Un exemple: le producteur a une seule file d'attente et envoie des messages toutes les 2 secondes:

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) 


})

Et voici un consommateur:

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

Si je lance le consommateur deux fois, Je peux voir que chaque consommateur consomme des messages alternatifs avec un comportement alternatif. Par exemple, je verrai les messages 1, 3, 5 dans un terminal, 2, 4, 6 dans la liste déroulante. autre.

Ma question est:

  • Puis-je demander à chaque consommateur de recevoir les mêmes messages? C'est-à-dire que les deux consommateurs reçoivent le message 1, 2, 3, 4, 5, 6? Comment cela s'appelle-t-il dans le langage AMQP/RabbitMQ? Comment est-il configuré normalement?

  • Est-ce couramment fait? Devrais-je simplement faire en sorte que le message achemine le message en deux files d'attente distinctes, avec un seul consommateur?

114
mikemaccana

Puis-je demander à chaque consommateur de recevoir les mêmes messages? C'est-à-dire que les deux consommateurs reçoivent le message 1, 2, 3, 4, 5, 6? Comment cela s'appelle-t-il dans le langage AMQP/RabbitMQ? Comment est-il configuré normalement?

Non, pas si les consommateurs sont dans la même file d'attente. De RabbitMQ Concepts AMQP guide:

il est important de comprendre que, dans l'AMQP 0-9-1, la charge des messages est équilibrée entre les consommateurs.

Cela semble impliquer que le comportement de round robin dans une file d'attente est donné et non configurable. En d'autres termes, des files d'attente distinctes sont nécessaires pour que le même identifiant de message soit traité par plusieurs consommateurs.

Est-ce couramment fait? Devrais-je simplement faire en sorte que le message achemine le message en deux files distinctes, avec un seul consommateur?

Non, ce n'est pas possible, une seule file d'attente/plusieurs consommateurs, chaque consommateur traitant le même ID de message n'est pas possible. Avoir l’échange achemine le message dans deux files d’attente distinctes est en effet préférable.

Comme je n'ai pas besoin d'un routage trop complexe, un échange de fanout se chargera de cela. Auparavant, je ne m'étais pas beaucoup concentré sur les échanges, car node-amqp avait la notion d'un "échange par défaut" permettant de publier directement des messages sur une connexion. Cependant, la plupart des messages AMQP sont publiés sur un échange spécifique.

Voici mon échange de fanout, envoi et réception:

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

Il suffit de lire le tutoriel rabbitmq . Vous publiez un message pour échanger, pas pour faire la queue; il est ensuite acheminé vers les files d'attente appropriées. Dans votre cas, vous devez lier une file d'attente distincte pour chaque consommateur. De cette façon, ils peuvent utiliser les messages de manière totalement indépendante.

20
driushkin

Les dernières réponses sont presque correctes - j'ai des tonnes d'applications qui génèrent des messages qui doivent se retrouver avec différents consommateurs, le processus est donc très simple.

Si vous souhaitez que plusieurs consommateurs reçoivent le même message, procédez comme suit.

Créez plusieurs files d'attente, une pour chaque application devant recevoir le message, dans chaque propriété de la file d'attente, "liez" une balise de routage avec l'échange amq.direct. Changez votre application de publication pour l'envoyer à amq.direct et utilisez le tag de routage (pas une file d'attente). AMQP copiera ensuite le message dans chaque file avec la même liaison. Fonctionne comme un charme :)

Exemple: disons que je génère une chaîne JSON, je la publie sur l’échange "amq.direct" à l’aide de la balise de routage "new-sales-order", j’ai une file d’attente pour mon application order_printer qui imprime la commande, j’ai un file d'attente pour mon système de facturation qui enverra une copie de la commande et facturera le client et je dispose d'un système d'archivage Web sur lequel j'archive les commandes pour des raisons d'historique/de conformité et j'ai une interface Web cliente sur laquelle les commandes sont suivies tandis que d'autres informations nous parviennent une commande.

Donc, mes files d'attente sont: order_printer, order_billing, order_archive et order_tracking. Toutes ont la balise de liaison "new-sales-order" qui leur est liée, les 4 obtiendront les données JSON.

C'est un moyen idéal d'envoyer des données sans que l'application de publication sache ou se soucie des applications réceptrices.

15
z900collector

Oui, chaque consommateur peut recevoir les mêmes messages. jetez un coup d'œil à 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

pour différentes façons de router les messages. Je sais qu'ils sont pour python et Java mais il est bon de comprendre les principes, de décider de ce que vous faites et de trouver ensuite comment le faire dans JS. Son On dirait que vous voulez faire une simple fanout ( tutorial ), qui envoie les messages à toutes les files d'attente connectées à l'échange.

La différence avec ce que vous faites et ce que vous voulez faire est essentiellement que vous allez configurer et échanger ou taper fanout. Les serveurs de sortie envoient tous les messages à toutes les files d'attente connectées. Chaque file d'attente aura un consommateur qui aura accès à tous les messages séparément.

Oui, cela est courant, c’est l’une des caractéristiques de l’AMPQ.

6
robthewolf

Le modèle d'envoi est une relation un à un. Si vous souhaitez "envoyer" à plusieurs destinataires, vous devriez utiliser le modèle pub/sub. Voir http://www.rabbitmq.com/tutorials/tutorial-three-python.html pour plus de détails.

6
Peter Ritchie

RabbitMQ/AMQP: file d'attente unique, plusieurs consommateurs pour le même message et actualisation de la page.

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

Il y a une option intéressante dans ce scénario que je n’ai pas trouvée dans les réponses ici.

Vous pouvez Nack avec la fonctionnalité "remettre en file d'attente" chez un consommateur pour les traiter dans un autre. D'une manière générale, ce n'est pas une bonne façon, mais peut-être que ce sera assez bien pour quelqu'un.

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

Et méfiez-vous des boucles (lorsque tous les concumers nack + remettent le message en attente)!

1
Alexus1024

Comme j'évalue votre cas est:

  • J'ai une file d'attente de messages (votre source pour la réception de messages, nommons-la q111)

  • J'ai plusieurs consommateurs et j'aimerais faire différentes choses avec le même message.

Votre problème ici est que pendant que 3 messages sont reçus par cette file d'attente, le message 1 est consommé par un consommateur A, les autres consommateurs B et C consomment les messages 2 et 3. Là où vous avez besoin d'une configuration où rabbitmq transmet les mêmes copies de ces trois messages (1,2,3) aux trois consommateurs connectés (A, B, C) simultanément.

Bien que de nombreuses configurations puissent être réalisées à cette fin, une méthode simple consiste à utiliser le concept suivant en deux étapes:

  • Utilisez un rabbitmq-shovel dynamique pour extraire les messages de la file d'attente souhaitée (q111) et les publier sur un échange fanout (échange exclusivement créé et dédié à cette fin).
  • Maintenant, reconfigurez vos clients A, B et C (qui écoutaient la file d'attente (q111)) pour écouter directement depuis cet échange Fanout en utilisant une file d'attente exclusive et anonyme pour chaque consommateur.

Remarque: lorsque vous utilisez ce concept, ne consommez pas directement à partir de la file d'attente source (q111), car les messages déjà consommés ne seront pas transférés à votre échange Fanout.

Si vous pensez que cela ne répond pas à vos besoins, n'hésitez pas à poster vos suggestions :-)

1
Anshuman Banerjee

Pour obtenir le comportement souhaité, il suffit que chaque consommateur consomme à partir de sa propre file d'attente. Vous devrez utiliser un type d'échange non direct (sujet, en-tête, fanout) pour que le message parvienne à toutes les files d'attente à la fois.

1
Skylos

Je pense que vous devriez vérifier l’envoi de vos messages en utilisant l’échangeur fan-out. De cette façon, vous recevrez le même message pour différents consommateurs. Sous le tableau, RabbitMQ crée différentes files d’attente pour chacun de ces nouveaux consommateurs/abonnés.

Ceci est le lien pour voir l’exemple de tutoriel en javascript https://www.rabbitmq.com/tutorials/tutorial-one-javascript.html

0
Alejandro Serret

S'il vous arrive d'utiliser la bibliothèque amqplib telle que je suis, ils ont un exemple pratique d'une implémentation de tutoriel Publier/S'abonner à RabbitMQ lequel vous pourriez trouver utile.

0
brettjonesdev