RabbitMQ / AMQP: tek bir sıra, aynı mesaj için birden fazla tüketici mi?


146

Genel olarak RabbitMQ ve AMQP kullanmaya başladım.

  • Bir mesaj kuyruğum var
  • Aynı mesajla farklı şeyler yapmak istediğim birden fazla tüketicim var .

RabbitMQ belgelerinin çoğu yuvarlak döngüye odaklanmış gibi görünmektedir, yani tek bir mesaj tek bir tüketici tarafından tüketilir ve yük her tüketici arasında yayılır. Bu gerçekten şahit olduğum davranış.

Örnek: yapımcının tek bir kuyruğu vardır ve her 2 saniyede bir mesaj gönderir:

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) 


})

Ve işte bir tüketici:

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

Tüketiciyi iki kez başlatırsam, her bir tüketicinin round-robin davranışında alternatif mesajlar tükettiğini görebilirim. Örneğin, bir terminalde 1, 3, 5, diğerinde 2, 4, 6 mesajlarını göreceğim .

Sorum şu:

  • Her tüketicinin aynı iletileri almasını sağlayabilir miyim? Yani, her iki tüketiciye de mesaj 1, 2, 3, 4, 5, 6 mı geliyor? AMQP / RabbitMQ konuşmasında buna ne denir? Normalde nasıl yapılandırılır?

  • Bu yaygın olarak mı yapılıyor? Değişimin, mesajı tek bir tüketici ile iki ayrı kuyruğa yönlendirmesi gerekir mi?


5
Ben RabbitMQ uzmanı değilim. Bununla birlikte, şimdi sahip olduğunuz sıraya denir, ancak istediğiniz şey konulardır, şu eğiticiye bakın: rabbitmq.com/tutorials/tutorial-five-python.html , kuyruklar ve konular hakkında daha fazla bilgi: msdn.microsoft.com/en-us /library/windowsazure/hh367516.aspx
UrbanEsc

1
Ben aslında fanout istediğini düşünüyorum ama konular da işe yarayacak ve daha sonra daha fazla kontrol verecek.
robthewolf

Teşekkürler @UrbanEsc. Konular bir mesajı birden fazla kuyruğa vurarak sorunu çözüyor gibi görünüyor ve bu nedenle her kuyruk tüketicisi tarafından tüketiliyor. Bu da benim özel durumum için çoklu kuyruk / tek tüketici senaryosuna daha da eğiliyor.
mikemaccana

1
2018 için (hatta 2016 ve öncesi için) cevap Kafka, IMO gibi bir şey kullanmaktır.
WattsInABox

Yanıtlar:


115

Her tüketicinin aynı iletileri almasını sağlayabilir miyim? Yani, her iki tüketiciye de mesaj 1, 2, 3, 4, 5, 6 mı geliyor? AMQP / RabbitMQ konuşmasında buna ne denir? Normalde nasıl yapılandırılır?

Hayır, tüketiciler aynı sıradaysa değil. RabbitMQ'nun AMQP Kavramları kılavuzundan:

AMQP 0-9-1'de mesajların tüketiciler arasında yük dengeli olduğunu anlamak önemlidir.

Bu , bir kuyruktaki round-robin davranışının belirli ve yapılandırılamaz olduğu anlamına gelir. Yani, aynı mesaj kimliğinin birden fazla tüketici tarafından ele alınması için ayrı kuyruklar gereklidir.

Bu yaygın olarak mı yapılıyor? Değişimin, mesajı tek bir tüketici ile iki ayrı kuyruğa yönlendirmesi gerekir mi?

Hayır, tek bir sıra / her bir tüketicinin aynı ileti kimliğini işleyen birden fazla tüketici mümkün değildir. Değişimin mesajı iki ayrı kuyruğa yönlendirmesi gerçekten daha iyidir.

Çok karmaşık bir yönlendirmeye ihtiyaç duymadığım için, bir fanout değişimi bunu güzel bir şekilde halledecek. Düğüm-amqp, doğrudan bağlantıya mesaj yayınlamanıza izin veren bir 'varsayılan değişim' kavramına sahip olduğundan, değiş tokuşlara çok fazla odaklanmadım, ancak çoğu AMQP mesajı belirli bir exchange'de yayınlandı.

İşte benim gönderi ve alma benim fanout borsası:

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 dışarı açıkça istediğini oldu. Burada size yardımcı olmaz, ama bir kuyruktaki yuvarlak-robin davranışından yapılandırılabilir olduğumu düşündüm. int prefetchCount = 1; channel.basicQos(prefetchCount); Bu, her tüketicinin bir öncekiyle biter bitmez bir mesaj almasını sağlayacaktır. Alternatif mesajlar almak yerine. Yine sorununuzu çözmez, ancak insanların bilmesi yararlı olabilir. Örnek burada http://www.rabbitmq.com/tutorials/tutorial-two-java.html Adil Gönderi
Ommit

3
Açıklığa kavuşturmak için: 'varsayılan değişim' düğüme-amqp'ye özgü değildir. Aşağıdaki kurallara sahip genel AMQP konseptidir: varsayılan alışverişe herhangi bir mesaj yayınlandığında, yönlendirme anahtarı (bu mesajın yayınlandığı) AMQP aracısı tarafından kuyruk adı olarak işlem görür. Yani doğrudan kuyruklarda yayınlayabilirsiniz. Ama sen değilsin. Aracı, her kuyruğu sıra adına eşit yönlendirme anahtarıyla varsayılan değiş tokuşa bağlar.
Ruslan Stelmachenko

2
Rabbitmq'de Apache activemq jms konularına, kuyrukların dahil olmadığı, ancak çok noktaya yayının dahil olduğu herhangi bir alternatif var mı?
pantonis

Aynı kullanıcı birden fazla cihazdan giriş yaparsa mesaj sadece bir cihaz alır. Nasıl çözülebilir veya herhangi bir fikir lütfen?
Rafiq

@Rafiq bunun için bir soru sormalısın.
mikemaccana

28

Sadece rabbitmq öğreticisini okuyun . İletiyi sıraya değil, değiş tokuşa gönderirsiniz; daha sonra uygun kuyruklara yönlendirilir. Sizin durumunuzda, her tüketici için ayrı bir kuyruk bağlamanız gerekir. Bu şekilde mesajları tamamen bağımsız olarak tüketebilirler.


27

Son birkaç cevap neredeyse doğru - farklı tüketicilerle sonuçlanması gereken mesajlar üreten tonlarca uygulamam var, bu yüzden süreç çok basit.

Aynı iletiye birden fazla tüketici istiyorsanız, aşağıdaki yordamı uygulayın.

İletiyi alacak her uygulama için bir tane olmak üzere birden çok kuyruk oluşturun, her kuyruk özelliklerinde, bir yönlendirme etiketini amq.direct exchange'i ile "bağlar". Yayımlama uygulamanızı amq.direct'e gönderecek ve yönlendirme etiketini (sıra değil) kullanacak şekilde değiştirin. AMQP daha sonra iletiyi aynı bağlama ile her kuyruğa kopyalar. Tıkır tıkır çalışıyor :)

Örnek: Oluşturduğum bir JSON dizesi var diyelim, "yeni satış-sipariş" yönlendirme etiketini kullanarak "amq.direct" değişiminde yayınlıyorum, sipariş yazdırır benim order_printer uygulaması için bir kuyruk var, ben bir Siparişin bir kopyasını gönderecek ve müşteriyi faturalayacak faturalandırma sistemim için kuyruk ve siparişleri geçmiş / uygunluk nedenleriyle arşivlediğim ve diğer bilgilerin geldiği gibi siparişlerin takip edildiği bir müşteri web arayüzüm var Bir sipariş.

Yani benim sıralarım: order_printer, order_billing, order_archive ve order_tracking Hepsi kendilerine bağlı "new-sales-order" bağlayıcı etiket var, 4 de JSON verilerini alacak.

Bu, yayınlama uygulaması alıcı uygulamaları bilmeden veya önemsemeden veri göndermenin ideal bir yoludur.


8

Evet, her tüketici aynı mesajları alabilir. http://www.rabbitmq.com/tutorials/tutorial-three-python.html http://www.rabbitmq.com/tutorials/tutorial-four-python.html http: //www.rabbitmq adresine bir göz atın . com / öğreticiler / öğretici-beş python.html

iletileri yönlendirmenin farklı yolları için. Python ve java için olduklarını biliyorum ama ilkeleri anlamak, ne yaptığınıza karar vermek ve daha sonra JS'de nasıl yapacağınızı bulmak iyi. Mesajları borsaya bağlı tüm sıralara gönderen basit bir fanout ( öğretici 3 ) yapmak istediğiniz gibi geliyor .

Ne yaptığınız ve ne yapmak istediğinizle ilgili temel olarak fanout kuracak ve değiştirecek ya da yazacaksınız. Fanout excahnges tüm iletileri bağlı tüm kuyruklara gönderir. Her kuyruk, tüm iletilere ayrı ayrı erişebilecek bir tüketiciye sahip olacaktır.

Evet, bu yaygın olarak yapılır, AMPQ'nun özelliklerinden biridir.


'Bu yaygın olarak yapılıyor mu?' 'Her tüketicinin aynı iletileri almasını sağlamaktan' bahsettim. Muhtemelen yeterince açık olmadığım için benim hatam.
mikemaccana

Aslında bunun ne için kullanmak istediğinize bağlı olduğunu söyleyebilirim. Pub / sub veya work tail olmak üzere iki temel seçeneğiniz vardır. Orijinal kurulumunuz bir çalışma kuyruğuydu ancak istediğiniz bir fanout pub / sub'dur. Burada ortak kullanımın tamamen ne yapmak istediğinize bağlı olduğu belirtiliyor.
robthewolf

Elbette ama bir çalışma kuyruğunda, aynı mesaj (örneğin, aynı mesaj kimliği) farklı tüketiciler tarafından ele alınmaz - dolaylı olarak robin. Yine bu muhtemelen yeterince açık olmadığım için benim hatam.
mikemaccana

burada çapraz amaçlarla konuşuyor gibi görünüyoruz.
robthewolf

Karışıklık için üzgünüm. Aynı sıradaki tüketicilerin aynı mesaj kimliğini işlediği bir çalışma kuyruğuna sahip olmanın bir yolu varsa, lütfen beni bir referansa yönlendirin. Aksi takdirde başka bir yerde okuduğuma inanmaya devam edeceğim.
mikemaccana


3

RabbitMQ / AMQP: tek sıra, aynı mesaj ve sayfa yenileme için birden fazla tüketici.

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

İstediğiniz davranışı elde etmek için, her bir tüketicinin kendi kuyruğundan tüketmesini sağlamanız yeterlidir. İletiyi bir kerede tüm kuyruklara almak için doğrudan olmayan bir exchange türü (konu, başlık, fanout) kullanmanız gerekir.


1

Davanızı değerlendirdiğim gibi:

  • Bir mesaj kuyruğum var (mesaj almak için kaynağınız, q111 adını verelim)

  • Aynı mesajla farklı şeyler yapmak istediğim birden fazla tüketicim var.

Buradaki sorun, bu kuyruk tarafından 3 ileti alınırken, ileti 1 bir tüketici A tarafından tüketilirken, diğer B ve C tüketicileri ileti 2 ve 3'ü tüketir. Rabbitmq'in aynı kopyalarından geçtiği bir kuruluma ihtiyacınız olduğu yerde tüm bu üç mesaj (1,2,3) üç bağlı tüketiciye de (A, B, C) aynı anda.

Bunu başarmak için birçok konfigürasyon yapılabilirken, basit bir yol aşağıdaki iki adım konseptini kullanmaktır:

  • İstenilen kuyruktan (q111) mesajları almak için dinamik bir tavşan kürek kullanın ve bir fanout değişiminde yayınlayın (yalnızca bu amaç için ayrılmış ve adanmış değişim).
  • Şimdi her bir tüketici için özel ve anonim bir kuyruk kullanarak doğrudan bu Fanout borsasından dinlemek için A, B & C (kuyruğu dinleyen (q111)) tüketicilerinizi yeniden yapılandırın.

Not: Bu kavramı kullanırken, doğrudan kaynak kuyruğundan (q111) tüketmeyin, çünkü önceden tüketilen mesajlar Fanout değişiminize kürek çekmeyecektir.

Bunun tam gereksiniminizi karşılamadığını düşünüyorsanız ... önerilerinizi göndermekten çekinmeyin :-)




-1

Burada cevaplarda bulamadığım bu senaryoda ilginç bir seçenek var.

Bir tüketicide "requeue" özelliği olan iletileri başka bir tüketicide işlemek için Nack kullanabilirsiniz. Genel olarak konuşmak doğru bir yol değildir, ama belki birisi için yeterince iyi olacaktır.

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

Ve döngülere dikkat edin (tüm concumers nack + mesajı requeue olduğunda)!


1
Hiçbir şekilde ölçeklenmediği için buna karşı son derece tavsiye ediyorum. Tüketiciler için herhangi bir emir yoktur, onu yeniden düzenlemeyecek olan B tüketicisini garanti edemez, işleyecek ve yeniden dolduracak olan tüketici A'dan önce mesajı alırsınız, bahsedilen döngüler bir problemdir. "Bu genellikle doğru yoldan değil" dediğiniz gibi, bunun diğer cevaplardan daha iyi olacağı bir senaryo düşünemiyorum.
Kevin Streicher
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.