RabbitMQ / AMQP: coda singola, più consumatori per lo stesso messaggio?


146

Ho appena iniziato a utilizzare RabbitMQ e AMQP in generale.

  • Ho una coda di messaggi
  • Ho più consumatori, che vorrei fare cose diverse con lo stesso messaggio .

La maggior parte della documentazione RabbitMQ sembra focalizzata sul round-robin, ovvero dove un singolo messaggio viene consumato da un singolo consumatore, con il carico che viene distribuito tra ciascun consumatore. Questo è davvero il comportamento a cui assisto.

Un esempio: il produttore ha una singola coda e invia messaggi ogni 2 secondi:

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) 


})

Ed ecco un consumatore:

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

Se avvio due volte il consumatore, vedo che ogni consumatore consuma messaggi alternativi nel comportamento round robin. Ad esempio, vedrò i messaggi 1, 3, 5 in un terminale, 2, 4, 6 nell'altro .

La mia domanda è:

  • Posso fare in modo che ciascun consumatore riceva gli stessi messaggi? Vale a dire, entrambi i consumatori ricevono il messaggio 1, 2, 3, 4, 5, 6? Come si chiama AMQP / RabbitMQ? Come viene normalmente configurato?

  • Questo è fatto comunemente? Devo solo fare in modo che lo scambio instradi il messaggio in due code separate, con un solo consumatore, invece?


5
Non sono un esperto di RabbitMQ. Tuttavia, quello che hai ora è chiamato coda ma ciò che vuoi sono gli argomenti, vedi questo tutorial: rabbitmq.com/tutorials/tutorial-five-python.html , più su code vs. argomenti: msdn.microsoft.com/en-us /library/windowsazure/hh367516.aspx
UrbanEsc

1
Credo che in realtà voglia scappare anche se gli argomenti funzioneranno anche e daranno più controllo in seguito.
robthewolf,

Grazie @UrbanEsc. Gli argomenti sembrano risolvere il problema facendo sì che un messaggio colpisca più code e quindi venga consumato dai consumatori di ciascuna coda. Il che mi spinge ulteriormente verso lo scenario a più code / singolo consumatore per il mio caso particolare.
mikemaccana,

1
Per il 2018 (e anche per il 2016 e precedenti) la risposta è usare qualcosa come Kafka, IMO.
WattsInABox,

Risposte:


115

Posso fare in modo che ciascun consumatore riceva gli stessi messaggi? Vale a dire, entrambi i consumatori ricevono il messaggio 1, 2, 3, 4, 5, 6? Come si chiama AMQP / RabbitMQ? Come viene normalmente configurato?

No, non se i consumatori si trovano sulla stessa coda. Dalla guida ai concetti AMQP di RabbitMQ :

è importante capire che, in AMQP 0-9-1, i messaggi sono bilanciati in base al carico tra i consumatori.

Ciò sembra implicare che il comportamento round-robin all'interno di una coda sia un dato , e non configurabile. Vale a dire, sono necessarie code separate per far sì che lo stesso ID messaggio sia gestito da più utenti.

Questo è fatto comunemente? Devo solo fare in modo che lo scambio instradi il messaggio in due code separate, con un solo consumatore, invece?

No, non è possibile, una singola coda / più consumatori con ciascuno dei consumatori che gestisce lo stesso ID messaggio non è possibile. Avere lo scambio indirizza il messaggio su due code separate è davvero meglio.

Poiché non ho bisogno di un routing troppo complesso, uno scambio in dissolvenza lo gestirà bene. Non mi ero concentrato troppo sugli scambi in precedenza poiché node-amqp ha il concetto di "scambio predefinito" che consente di pubblicare direttamente i messaggi su una connessione, tuttavia la maggior parte dei messaggi AMQP sono pubblicati in uno scambio specifico.

Ecco il mio scambio di fanout, sia inviando che ricevendo:

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
fan out era chiaramente quello che volevi. Non ti aiuterebbe qui, ma ho pensato di menzionare il comportamento del round robin all'interno di una coda è configurabile. int prefetchCount = 1; channel.basicQos(prefetchCount); Ciò consentirà a ciascun consumatore di ricevere un messaggio non appena termina con quello precedente. Invece di ricevere messaggi alternati. Ancora una volta non risolve il problema, ma potrebbe essere utile che le persone lo sappiano. esempio qui http://www.rabbitmq.com/tutorials/tutorial-two-java.html sotto Fair Dispatch
Ommit

3
Per chiarire: "scambio predefinito" non è specifico per nodo-amqp. È il concetto generale di AMQP con le seguenti regole: quando un messaggio viene pubblicato in uno scambio predefinito, la chiave di routing (con la quale quel messaggio viene pubblicato) viene trattata come nome della coda dal broker AMQP. Quindi sembra che tu possa pubblicare direttamente nelle code. Ma non lo sei. Il broker semplicemente associa ogni coda allo scambio predefinito con una chiave di routing uguale al nome della coda.
Ruslan Stelmachenko,

2
Esiste un'alternativa agli argomenti jms di Apache activemq in rabbitmq in cui non sono coinvolte le code ma piuttosto il multicast?
pantonis,

Se lo stesso utente accede da più dispositivi, il messaggio ottiene un solo dispositivo. Come si può risolvere o per favore qualche idea?
Rafiq,

@Rafiq dovresti fare una domanda per questo.
mikemaccana,

28

Basta leggere il tutorial di rabbitmq . Pubblica un messaggio da scambiare, non da mettere in coda; viene quindi indirizzato alle code appropriate. Nel tuo caso, dovresti associare una coda separata per ciascun consumatore. In questo modo, possono consumare messaggi in modo completamente indipendente.


27

Le ultime due risposte sono quasi corrette: ho tonnellate di app che generano messaggi che devono finire con consumatori diversi, quindi il processo è molto semplice.

Se si desidera che più utenti ricevano lo stesso messaggio, attenersi alla seguente procedura.

Crea più code, una per ogni app che deve ricevere il messaggio, in ciascuna proprietà della coda, "associa" un tag di routing con lo scambio amq.direct. Cambia l'app di pubblicazione per inviarla a amq.direct e usa il tag di routing (non una coda). AMQP copierà quindi il messaggio in ciascuna coda con lo stesso binding. Funziona come un fascino :)

Esempio: diciamo che ho una stringa JSON che ho generato, la pubblico nello scambio "amq.direct" usando il tag di routing "new-sales-order", ho una coda per la mia app order_printer che stampa l'ordine, ho un fare la fila per il mio sistema di fatturazione che invierà una copia dell'ordine e fatturerà al cliente e ho un sistema di archiviazione web in cui archivio gli ordini per motivi storici / di conformità e ho un'interfaccia web del cliente in cui gli ordini vengono tracciati man mano che arrivano altre informazioni un ordine.

Quindi le mie code sono: order_printer, order_billing, order_archive e order_tracking Tutti hanno il tag di associazione "new-sales-order" associato a loro, tutti e 4 otterranno i dati JSON.

Questo è un modo ideale per inviare dati senza che l'app di pubblicazione sappia o si preoccupi delle app di ricezione.


8

Sì, ogni consumatore può ricevere gli stessi messaggi. dai un'occhiata a http://www.rabbitmq.com/tutorials/tutorial-three-python.html http://www.rabbitmq.com/tutorials/tutorial-four-python.html http: //www.rabbitmq. com / tutorial / esercitazione-cinque python.html

per modi diversi di instradare i messaggi. So che sono per Python e Java, ma è bene capire i principi, decidere cosa stai facendo e poi trovare come farlo in JS. Sembra che tu voglia fare un semplice fanout ( tutorial 3 ), che invia i messaggi a tutte le code collegate allo scambio.

La differenza tra ciò che stai facendo e ciò che vuoi fare è fondamentalmente che stai per impostare e scambiare o digitare Fanout. Gli escahnges di Fanout inviano tutti i messaggi a tutte le code connesse. Ogni coda avrà un consumatore che avrà accesso a tutti i messaggi separatamente.

Sì, questo è comunemente fatto, è una delle caratteristiche di AMPQ.


ottima risposta, tranne per "è fatto comunemente?" Mi riferivo a "far sì che ogni consumatore ricevesse gli stessi messaggi", cosa che di solito non viene eseguita (i consumatori nella stessa coda sono sempre in round robin). Probabilmente è colpa mia per non essere abbastanza chiaro.
mikemaccana,

In realtà mi permetto di dire che dipende da cosa vuoi usarlo. Hai due scelte di base pub / sub o code di lavoro. La tua configurazione originale era una coda di lavoro, ma quello che volevi era un pub / sub fanout. Indicano che l'uso comune qui dipende totalmente da ciò che si desidera fare.
robthewolf,

Certo, ma in una coda di lavoro, lo stesso messaggio (ad es. Lo stesso ID messaggio) non è gestito da utenti diversi, è implicitamente round robin. Anche in questo caso è probabilmente colpa mia per non essere abbastanza chiaro.
mikemaccana,

sembra che stiamo parlando a scopi incrociati qui.
robthewolf,

Mi dispiace per la confusione. Se esiste un modo per avere una coda di lavoro in cui i consumatori sulla stessa coda gestiscono lo stesso ID messaggio, per favore indicami un riferimento. Altrimenti continuerò a credere a ciò che ho letto altrove.
mikemaccana,


3

RabbitMQ / AMQP: coda singola, più utenti per lo stesso messaggio e aggiornamento della pagina.

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

Per ottenere il comportamento desiderato, è sufficiente far consumare ogni consumatore dalla propria coda. Dovrai utilizzare un tipo di scambio non diretto (argomento, intestazione, fanout) per inviare il messaggio a tutte le code contemporaneamente.


1

Mentre valuto il tuo caso è:

  • Ho una coda di messaggi (la tua fonte per ricevere messaggi, chiamiamola q111)

  • Ho più consumatori, che vorrei fare cose diverse con lo stesso messaggio.

Il tuo problema qui è mentre 3 messaggi vengono ricevuti da questa coda, il messaggio 1 viene consumato da un consumatore A, gli altri consumatori B e C consumano i messaggi 2 e 3. Dove hai bisogno di una configurazione in cui rabbitmq passa sulle stesse copie di tutti questi tre messaggi (1,2,3) a tutti e tre i consumatori collegati (A, B, C) contemporaneamente.

Sebbene sia possibile effettuare molte configurazioni per raggiungere questo obiettivo, un modo semplice è utilizzare il seguente concetto in due fasi:

  • Utilizzare una pala rabbitmq dinamica per prelevare i messaggi dalla coda desiderata (q111) e pubblicare in uno scambio in dissolvenza (scambio creato esclusivamente e dedicato a questo scopo).
  • Ora riconfigura i tuoi consumatori A, B & C (che stavano ascoltando la coda (q111)) per ascoltare da questo scambio Fanout direttamente usando una coda esclusiva e anonima per ciascun consumatore.

Nota: durante l'utilizzo di questo concetto non consumare direttamente dalla coda di origine (q111), poiché i messaggi già consumati non verranno trasferiti nello scambio di Fanout.

Se ritieni che questo non soddisfi le tue esatte esigenze ... non esitare a pubblicare i tuoi suggerimenti :-)




-1

C'è una opzione interessante in questo scenario che non ho trovato nelle risposte qui.

Puoi nackare i messaggi con la funzione "requeue" in un consumatore per elaborarli in un altro. In generale, non è un modo giusto, ma forse sarà abbastanza buono per qualcuno.

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

E fai attenzione ai loop (quando tutti i partecipanti nack + requeue message)!


1
Consiglio vivamente di non farlo, poiché non si adatta in alcun modo. Non vi è alcun ordine per i consumatori, non è possibile garantire al consumatore B che non lo sostituirà, riceverà il messaggio prima che il consumatore A lo elaborerà e lo sostituirà, i cicli citati sono un problema. Come dici "questo generalmente non è il modo giusto", e non riesco a pensare a uno scenario in cui questo sarebbe meglio delle altre risposte.
Kevin Streicher,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.