RabbitMQ / AMQP: ¿cola única, múltiples consumidores para el mismo mensaje?

145

Estoy empezando a usar RabbitMQ y AMQP en general.

  • Tengo una cola de mensajes
  • Tengo varios consumidores, y me gustaría hacer cosas diferentes con el mismo mensaje .

La mayor parte de la documentación de RabbitMQ parece estar centrada en round-robin, es decir, donde un solo mensaje es consumido por un solo consumidor, y la carga se distribuye entre cada consumidor. Este es de hecho el comportamiento que presencio.

Un ejemplo: el productor tiene una sola cola y envía mensajes cada 2 segundos:

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) 


})

Y aquí hay un consumidor:

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 inicio al consumidor dos veces, puedo ver que cada consumidor está consumiendo mensajes alternativos en el comportamiento de todos contra todos. Por ejemplo, veré los mensajes 1, 3, 5 en una terminal, 2, 4, 6 en la otra .

Mi pregunta es:

  • ¿Puedo hacer que cada consumidor reciba los mismos mensajes? Es decir, ¿ambos consumidores reciben el mensaje 1, 2, 3, 4, 5, 6? ¿Cómo se llama esto en AMQP / RabbitMQ? ¿Cómo se configura normalmente?

  • ¿Se hace esto comúnmente? ¿Debería hacer que el intercambio enrute el mensaje en dos colas separadas, con un solo consumidor?

mikemaccana
fuente
55
No soy un experto en RabbitMQ. Sin embargo, lo que ahora tiene se llama cola, pero lo que quiere son temas, consulte este tutorial: rabbitmq.com/tutorials/tutorial-five-python.html , más sobre colas frente a temas: msdn.microsoft.com/en-us /library/windowsazure/hh367516.aspx
UrbanEsc
1
Creo que quiere fanout en realidad, aunque los temas también funcionarán y darán más control más adelante.
robthewolf
Gracias @UrbanEsc. Los temas parecen resolver el problema haciendo que un mensaje llegue a varias colas y, por lo tanto, sea consumido por cada consumidor de colas. Lo que me inclina más hacia el escenario de colas múltiples / consumidor único para mi caso particular.
mikemaccana
1
Para 2018 (e incluso para 2016 y anteriores) la respuesta es usar algo como Kafka, IMO.
WattsInABox

Respuestas:

115

¿Puedo hacer que cada consumidor reciba los mismos mensajes? Es decir, ¿ambos consumidores reciben el mensaje 1, 2, 3, 4, 5, 6? ¿Cómo se llama esto en AMQP / RabbitMQ? ¿Cómo se configura normalmente?

No, no si los consumidores están en la misma cola. De la guía de conceptos AMQP de RabbitMQ :

Es importante comprender que, en AMQP 0-9-1, los mensajes tienen un equilibrio de carga entre los consumidores.

Esto parece implicar que el comportamiento de round-robin dentro de una cola es un hecho y no es configurable. Es decir, se requieren colas separadas para que varios consumidores manejen la misma ID de mensaje.

¿Se hace esto comúnmente? ¿Debería hacer que el intercambio enrute el mensaje en dos colas separadas, con un solo consumidor?

No, no lo es, una sola cola / múltiples consumidores con cada consumidor manejando la misma ID de mensaje no es posible. Hacer que el intercambio encamine el mensaje en dos colas separadas es realmente mejor.

Como no necesito un enrutamiento demasiado complejo, un intercambio fanout lo manejará muy bien. No me centré demasiado en los intercambios antes, ya que node-amqp tiene el concepto de un 'intercambio predeterminado' que le permite publicar mensajes en una conexión directamente, sin embargo, la mayoría de los mensajes AMQP se publican en un intercambio específico.

Aquí está mi intercambio de fanout, tanto de envío como de recepción:

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) 
 })
})
mikemaccana
fuente
1
abanicar era claramente lo que querías. No te ayudaría aquí, pero pensé que mencionaría que el comportamiento de round robin dentro de una cola es configurable. int prefetchCount = 1; channel.basicQos(prefetchCount); Esto permitirá que cada consumidor reciba un mensaje tan pronto como termine con el anterior. En lugar de recibir mensajes alternos. Nuevamente no resuelve su problema, pero podría ser útil para que la gente lo sepa. ejemplo aquí http://www.rabbitmq.com/tutorials/tutorial-two-java.html bajo Fair Dispatch
Ommit
3
Para aclarar: 'intercambio predeterminado' no es específico de nodo-amqp. Es un concepto general de AMQP con las siguientes reglas: cuando cualquier mensaje publicado en el intercambio predeterminado, la clave de enrutamiento (con la que se publicó ese mensaje) es tratada como el nombre de la cola por el agente de AMQP. Por lo tanto, parece que puede publicar en colas directamente. Pero no lo eres. El intermediario simplemente vincula cada cola al intercambio predeterminado con una clave de enrutamiento igual al nombre de la cola.
Ruslan Stelmachenko
2
¿Hay alguna alternativa a los temas Apache activemq jms en rabbitmq donde no hay colas involucradas sino más bien multidifusión?
pantonis
Si el mismo usuario inicia sesión desde múltiples dispositivos, entonces el mensaje obtiene solo un dispositivo. ¿Cómo se puede resolver o alguna idea, por favor?
Rafiq
@ Rafiq deberías hacer una pregunta para esto.
mikemaccana
28

Solo lea el tutorial de rabbitmq . Publica mensajes para intercambiar, no para hacer cola; luego se enruta a las colas apropiadas. En su caso, debe vincular una cola separada para cada consumidor. De esa manera, pueden consumir mensajes de forma completamente independiente.

driushkin
fuente
27

Las últimas dos respuestas son casi correctas: tengo toneladas de aplicaciones que generan mensajes que deben terminar con diferentes consumidores, por lo que el proceso es muy simple.

Si desea que varios consumidores reciban el mismo mensaje, realice el siguiente procedimiento.

Cree varias colas, una para cada aplicación que recibirá el mensaje, en cada una de las propiedades de la cola, "enlace" una etiqueta de enrutamiento con el intercambio amq.direct. Cambie su aplicación de publicación para enviar a amq.direct y use la etiqueta de enrutamiento (no una cola). AMQP luego copiará el mensaje en cada cola con el mismo enlace. Funciona de maravilla :)

Ejemplo: Digamos que tengo una cadena JSON que genero, la publico en el intercambio "amq.direct" usando la etiqueta de enrutamiento "new-sales-order", tengo una cola para mi aplicación order_printer que imprime el pedido, tengo un cola para mi sistema de facturación que enviará una copia del pedido y facturará al cliente y tengo un sistema de archivo web donde archivo los pedidos por razones históricas / de cumplimiento y tengo una interfaz web del cliente donde se realiza un seguimiento de los pedidos a medida que se recibe más información una orden.

Por lo tanto, mis colas son: order_printer, order_billing, order_archive y order_tracking. Todos tienen la etiqueta de enlace "new-sales-order" unida a ellos, los 4 obtendrán los datos JSON.

Esta es una forma ideal de enviar datos sin que la aplicación de publicación conozca o se preocupe por las aplicaciones receptoras.

colector z900
fuente
8

Sí, cada consumidor puede recibir los mismos mensajes. eche un vistazo a 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

para diferentes formas de enrutar mensajes. Sé que son para Python y Java, pero es bueno entender los principios, decidir lo que está haciendo y luego encontrar cómo hacerlo en JS. Parece que quieres hacer un simple fanout ( tutorial 3 ), que envía los mensajes a todas las colas conectadas al intercambio.

La diferencia con lo que está haciendo y lo que quiere hacer es básicamente que va a configurar e intercambiar o escribir fanout. Los despliegues de Fanout envían todos los mensajes a todas las colas conectadas. Cada cola tendrá un consumidor que tendrá acceso a todos los mensajes por separado.

Sí, esto se hace comúnmente, es una de las características de AMPQ.

robthewolf
fuente
gran respuesta, excepto por '¿se hace esto comúnmente?' Me refería a 'hacer que cada consumidor reciba los mismos mensajes', lo cual no se hace comúnmente (los consumidores en la misma cola siempre hacen turnos). Probablemente es mi culpa por no ser lo suficientemente claro.
mikemaccana
En realidad, me aventuraría a decir que depende de para qué lo quieras usar. Tiene dos opciones básicas de pub / sub o colas de trabajo. Tu configuración original era una cola de trabajo, pero lo que querías era un pub / sub fanático. Señalan que el uso común aquí depende totalmente de lo que quieras hacer.
robthewolf
Claro, pero en una cola de trabajo, el mismo mensaje (por ejemplo, la misma ID de mensaje) no es manejado por diferentes consumidores, es implícitamente round robin. De nuevo, probablemente sea mi culpa por no ser lo suficientemente claro.
mikemaccana
Parece que estamos hablando de propósitos cruzados aquí.
robthewolf
Perdón por la confusión. Si hay alguna forma de tener una cola de trabajo donde los consumidores en la misma cola manejen la misma ID de mensaje, por favor, indíqueme una referencia. De lo contrario, seguiré creyendo lo que he leído en otros lugares.
mikemaccana
3

RabbitMQ / AMQP: cola única, múltiples consumidores para el mismo mensaje y actualización de página.

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);
            }
        });
    });
durai
fuente
1

Para obtener el comportamiento que desea, simplemente haga que cada consumidor consuma de su propia cola. Tendrá que usar un tipo de intercambio no directo (tema, encabezado, despliegue) para que el mensaje llegue a todas las colas a la vez.

Skylos
fuente
1

Como evalúo su caso es:

  • Tengo una cola de mensajes (su fuente para recibir mensajes, nombremos q111)

  • Tengo varios consumidores, y me gustaría hacer cosas diferentes con el mismo mensaje.

Su problema aquí es que mientras esta cola recibe 3 mensajes, el consumidor 1 consume el mensaje 1, otros consumidores B y C consumen los mensajes 2 y 3. Cuando necesite una configuración en la que rabbitmq pase las mismas copias de todos estos tres mensajes (1,2,3) a los tres consumidores conectados (A, B, C) simultáneamente.

Si bien se pueden hacer muchas configuraciones para lograr esto, una forma simple es usar el siguiente concepto de dos pasos:

  • Use una pala de conejo dinámica para recoger mensajes de la cola deseada (q111) y publíquela en un intercambio fanout (intercambio creado y dedicado exclusivamente para este propósito).
  • Ahora vuelva a configurar sus consumidores A, B y C (que estaban escuchando la cola (q111)) para escuchar directamente de este intercambio Fanout utilizando una cola exclusiva y anónima para cada consumidor.

Nota: Al usar este concepto, no consuma directamente de la cola de origen (q111), ya que los mensajes ya consumidos no se enviarán a su intercambio Fanout.

Si cree que esto no satisface sus requisitos exactos ... no dude en publicar sus sugerencias :-)

Anshuman Banerjee
fuente
0

Creo que deberías revisar el envío de tus mensajes usando el despliegue intercambiador de . De esta manera, recibirá el mismo mensaje para diferentes consumidores, debajo de la tabla RabbitMQ está creando diferentes colas para cada uno de estos nuevos consumidores / suscriptores.

Este es el enlace para ver el ejemplo del tutorial en javascript https://www.rabbitmq.com/tutorials/tutorial-one-javascript.html

Alejandro Serret
fuente
-1

Hay una opción interesante en este escenario que no he encontrado en las respuestas aquí.

Puede bloquear mensajes con la función "solicitar" en un consumidor para procesarlos en otro. En términos generales, no es una forma correcta, pero tal vez sea lo suficientemente bueno para alguien.

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

¡Y cuidado con los bucles (cuando todos los concumers nack + mensaje de solicitud)!

Alexus1024
fuente
1
Recomiendo encarecidamente esto, ya que no escala de ninguna manera. No hay un pedido para los consumidores, no se puede garantizar al consumidor B que no lo solicitará, recibirá el mensaje antes que el consumidor A que lo procese y solicite, los bucles mencionados son un problema. Como usted dice "esto generalmente no está hablando de la manera correcta", y no puedo pensar en un escenario en el que esto sería mejor que las otras respuestas.
Kevin Streicher