RabbitMQ / AMQP: คิวเดียวผู้บริโภคหลายรายสำหรับข้อความเดียวกันหรือไม่


146

ฉันเพิ่งเริ่มใช้ RabbitMQ และ AMQP โดยทั่วไป

  • ฉันมีคิวข้อความ
  • ฉันมีผู้บริโภคหลายที่ผมอยากจะทำสิ่งที่แตกต่างกับข้อความเดียวกัน

เอกสาร RabbitMQ ส่วนใหญ่ดูเหมือนว่าจะเน้นไปที่ round-robin นั่นคือการบริโภคข้อความเดียวโดยผู้บริโภครายเดียวโดยมีการกระจายโหลดระหว่างผู้ใช้แต่ละราย นี่คือพฤติกรรมที่ฉันเป็นพยาน

ตัวอย่าง: ผู้สร้างมีคิวเดียวและส่งข้อความทุก 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)
    })
  })
})

หากฉันเริ่มผู้บริโภคสองครั้งฉันจะเห็นว่าผู้บริโภคแต่ละรายบริโภคข้อความทางเลือกในลักษณะการปัดเศษ เช่นฉันจะเห็นข้อความที่ 1, 3, 5 ในหนึ่ง terminal, 2, 4, 6 ในอื่น ๆ

คำถามของฉันคือ:

  • ฉันสามารถให้ผู้บริโภคแต่ละคนได้รับข้อความเดียวกันได้หรือไม่? คือผู้บริโภคทั้งสองได้รับข้อความ 1, 2, 3, 4, 5, 6? สิ่งนี้เรียกว่าอะไรใน AMQP / RabbitMQ พูด ปกติแล้วมันเป็นวิธีการกำหนดค่า?

  • เป็นสิ่งปกติหรือไม่ ฉันควรให้เส้นทางแลกเปลี่ยนข้อความเป็นสองคิวแยกกันโดยมีผู้บริโภครายเดียวหรือไม่


5
ฉันไม่ใช่ผู้เชี่ยวชาญ RabbitMQ อย่างไรก็ตามสิ่งที่คุณมีในขณะนี้เรียกว่าคิว แต่สิ่งที่คุณต้องการคือหัวข้อดูบทช่วยสอนนี้: rabbitmq.com/tutorials/tutorial-five-python.htmlเพิ่มเติมเกี่ยวกับคิวและหัวข้อ: msdn.microsoft.com/en-us /library/windowsazure/hh367516.aspx
UrbanEsc

1
ฉันเชื่อว่าเขาต้องการงาน fanout จริง ๆ แม้ว่าหัวข้อจะทำงานได้ดีและจะให้การควบคุมเพิ่มเติมในภายหลัง
robthewolf

ขอบคุณ @UrbanEsc ดูเหมือนว่าหัวข้อจะแก้ปัญหาโดยให้ข้อความเดียวมีหลายคิวและจะถูกบริโภคโดยผู้บริโภคแต่ละคิว ซึ่งโน้มตัวฉันต่อไปสู่สถานการณ์หลายคิว / สถานการณ์ผู้บริโภคเดี่ยวสำหรับกรณีเฉพาะของฉัน
mikemaccana

1
สำหรับปี 2018 (และแม้กระทั่งสำหรับปี 2016 และก่อนหน้านี้) คำตอบคือใช้บางอย่างเช่นคาฟคา, IMO
WattsInABox

คำตอบ:


115

ฉันสามารถให้ผู้บริโภคแต่ละคนได้รับข้อความเดียวกันได้หรือไม่? คือผู้บริโภคทั้งสองได้รับข้อความ 1, 2, 3, 4, 5, 6? สิ่งนี้เรียกว่าอะไรใน AMQP / RabbitMQ พูด ปกติแล้วมันเป็นวิธีการกำหนดค่า?

ไม่ไม่ใช่หากผู้บริโภคอยู่ในคิวเดียวกัน จากแนวทางแนวคิด AMQPของ RabbitMQ :

สิ่งสำคัญคือต้องเข้าใจว่าใน AMQP 0-9-1 ข้อความนั้นมีความสมดุลระหว่างผู้บริโภค

สิ่งนี้ดูเหมือนจะบอกเป็นนัยว่าพฤติกรรมการปัดเศษรอบในคิวนั้นเป็นสิ่งที่กำหนดและไม่สามารถกำหนดค่า นั่นคือจำเป็นต้องมีคิวแยกต่างหากเพื่อให้สามารถจัดการ ID ข้อความเดียวกันได้โดยผู้ใช้หลายคน

เป็นสิ่งปกติหรือไม่ ฉันควรให้เส้นทางแลกเปลี่ยนข้อความเป็นสองคิวแยกกันโดยมีผู้บริโภครายเดียวหรือไม่

ไม่เลยไม่ใช่คิวเดียว / ผู้ใช้หลายคนที่มีผู้ใช้แต่ละรายจัดการ ID ข้อความเดียวกันไม่สามารถทำได้ การมีเส้นทางการแลกเปลี่ยนข้อความไปสู่สองคิวที่แยกจากกันนั้นดีกว่าจริง ๆ

เนื่องจากฉันไม่ต้องการการกำหนดเส้นทางที่ซับซ้อนเกินไปการแลกเปลี่ยน fanoutจะจัดการเรื่องนี้ได้เป็นอย่างดี ฉันไม่ได้มุ่งเน้นไปที่การแลกเปลี่ยนก่อนหน้านี้มากนักเนื่องจาก node-amqp มีแนวคิดของ 'การแลกเปลี่ยนเริ่มต้น' ที่ช่วยให้คุณสามารถเผยแพร่ข้อความไปยังการเชื่อมต่อโดยตรงอย่างไรก็ตามข้อความ AMQP ส่วนใหญ่ถูกเผยแพร่ไปยังการแลกเปลี่ยนที่เฉพาะเจาะจง

นี่คือการแลกเปลี่ยน fanout ของฉันทั้งการส่งและรับ:

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ภายใต้การจัดส่งที่เป็นธรรม
Ommit

3
ในการชี้แจง: 'การแลกเปลี่ยนเริ่มต้น' ไม่ใช่การระบุโหนด-amqp เป็นแนวคิดทั่วไปของ AMQP โดยมีกฎต่อไปนี้: เมื่อข้อความใด ๆ ที่เผยแพร่ไปยังการแลกเปลี่ยนเริ่มต้นคีย์การกำหนดเส้นทาง (ซึ่งข้อความนั้นเผยแพร่) ถือว่าเป็นชื่อคิวโดยโบรกเกอร์ AMQP ดังนั้นดูเหมือนว่าคุณสามารถเผยแพร่ไปยังคิวได้โดยตรง แต่คุณไม่ได้ นายหน้าเพียงผูกแต่ละคิวเพื่อแลกเปลี่ยนเริ่มต้นด้วยการกำหนดเส้นทางคีย์เท่ากับชื่อคิว
Ruslan Stelmachenko

2
มีทางเลือกอื่นสำหรับ Apache activemq jms ใน Rabbitmq โดยที่ไม่มีการเข้าคิว แต่เป็นการมัลติคาสท์?
pantonis

หากผู้ใช้เดียวกันเข้าสู่ระบบจากหลายอุปกรณ์แล้วข้อความได้รับเพียงหนึ่งอุปกรณ์จะสามารถแก้ไขได้หรือความคิดใด ๆ ได้โปรด?
Rafiq

@Rafiq คุณควรถามคำถามเกี่ยวกับเรื่องนี้
mikemaccana

28

เพียงอ่านrabbitmq กวดวิชา คุณเผยแพร่ข้อความเพื่อแลกเปลี่ยนไม่ใช่เพื่อเข้าคิว มันจะถูกส่งไปยังคิวที่เหมาะสม ในกรณีของคุณคุณควรผูกคิวแยกกันสำหรับผู้บริโภคแต่ละราย ด้วยวิธีนี้พวกเขาสามารถใช้ข้อความได้อย่างอิสระอย่างสมบูรณ์


27

คำตอบสุดท้ายสองคำเกือบถูกต้อง - ฉันมีแอพมากมายที่สร้างข้อความที่ต้องจบด้วยผู้บริโภคที่แตกต่างกันดังนั้นกระบวนการจึงง่ายมาก

หากคุณต้องการให้ผู้บริโภคหลายคนมีข้อความเดียวกันให้ทำตามขั้นตอนต่อไปนี้

สร้างหลายคิวหนึ่งรายการสำหรับแต่ละแอปที่จะรับข้อความในแต่ละคุณสมบัติของคิว "ผูก" แท็กการกำหนดเส้นทางด้วยการแลกเปลี่ยน amq.direct เปลี่ยนแอปที่คุณเผยแพร่เพื่อส่งไปยัง amq.direct และใช้ routing-tag (ไม่ใช่คิว) AMQP จะคัดลอกข้อความไปยังแต่ละคิวด้วยการเชื่อมโยงเดียวกัน ทำงานเหมือนมีเสน่ห์ :)

ตัวอย่าง: ให้บอกว่าฉันมีสตริง JSON ที่ฉันสร้างขึ้นฉันเผยแพร่มันเพื่อแลกเปลี่ยน "amq.direct" โดยใช้แท็กการกำหนดเส้นทาง "สั่งขายใหม่" ฉันมีคิวสำหรับแอป order_printer ของฉันที่พิมพ์คำสั่งฉันมี คิวสำหรับระบบการเรียกเก็บเงินของฉันที่จะส่งสำเนาคำสั่งซื้อและใบแจ้งหนี้ให้กับลูกค้าและฉันมีระบบเก็บถาวรเว็บที่ฉันเก็บถาวรคำสั่งซื้อด้วยเหตุผลทางประวัติศาสตร์ / การปฏิบัติตามกฎระเบียบและฉันมีเว็บอินเตอร์เฟสของลูกค้า คำสั่ง

ดังนั้นคิวของฉันคือ: 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 co.th / บทเรียน / กวดวิชาห้า python.html

สำหรับวิธีต่างๆในการจัดเส้นทางข้อความ ฉันรู้ว่ามันใช้สำหรับ python และ java แต่ก็เข้าใจหลักการได้ดีตัดสินใจว่าคุณกำลังทำอะไรอยู่และจากนั้นหาวิธีทำมันใน JS ดูเหมือนว่าคุณต้องการทำ fanout อย่างง่าย ( บทช่วยสอน 3 ) ซึ่งส่งข้อความไปยังคิวทั้งหมดที่เชื่อมต่อกับการแลกเปลี่ยน

ความแตกต่างกับสิ่งที่คุณกำลังทำและสิ่งที่คุณต้องการทำนั้นเป็นพื้นฐานที่คุณจะต้องตั้งค่าและแลกเปลี่ยนหรือพิมพ์ fanout การแสดง Fanout ส่งข้อความทั้งหมดไปยังคิวที่เชื่อมต่อทั้งหมด แต่ละคิวจะมีคอนซูเมอร์ที่สามารถเข้าถึงข้อความทั้งหมดแยกจากกัน

ใช่มันเป็นเรื่องปกติมันเป็นหนึ่งในคุณสมบัติของ AMPQ


คำตอบที่ดียกเว้น 'จะทำเช่นนี้หรือไม่' ฉันหมายถึง 'ให้ผู้บริโภคแต่ละรายได้รับข้อความเดียวกัน' ซึ่งไม่ได้ทำกันโดยทั่วไป (ผู้บริโภคในคิวเดียวกันมักจะวนรอบโรบิน) อาจเป็นความผิดของฉันที่ไม่ชัดเจนพอ
mikemaccana

อันที่จริงฉันอยากจะบอกว่ามันขึ้นอยู่กับสิ่งที่คุณต้องการใช้สำหรับ คุณมีสองตัวเลือกพื้นฐาน pub / sub หรือคิวงาน การตั้งค่าดั้งเดิมของคุณคือคิวงาน แต่สิ่งที่คุณต้องการคือผับ fanout / ย่อย ประเด็นก็คือการใช้งานทั่วไปที่นี่ขึ้นอยู่กับสิ่งที่คุณต้องการทำ
robthewolf

แน่นอน แต่ในคิวงานข้อความเดียวกัน (เช่นรหัสข้อความเดียวกัน) ไม่ได้รับการจัดการโดยผู้บริโภคที่แตกต่างกัน - มันคือโรบินรอบโดยปริยาย อีกครั้งนี่อาจเป็นความผิดของฉันที่ไม่ชัดเจนพอ
mikemaccana

ดูเหมือนว่าเรากำลังพูดถึงจุดประสงค์ไขว้ที่นี่
robthewolf

ขออภัยเกี่ยวกับความสับสน หากมีวิธีการมีคิวงานที่ผู้บริโภคในคิวเดียวกันจัดการ ID ข้อความเดียวกันโปรดชี้ให้ฉันอ้างอิง มิฉะนั้นฉันก็จะยังเชื่อในสิ่งที่ฉันได้อ่านที่อื่น
mikemaccana

7

รูปแบบการส่งเป็นความสัมพันธ์แบบหนึ่งต่อหนึ่ง หากคุณต้องการ "ส่ง" ไปยังผู้รับมากกว่าหนึ่งคนคุณควรใช้รูปแบบ pub / sub ดูhttp://www.rabbitmq.com/tutorials/tutorial-three-python.htmlสำหรับรายละเอียดเพิ่มเติม


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

ในการรับพฤติกรรมที่คุณต้องการเพียงให้ผู้บริโภคแต่ละคนบริโภคจากคิวของตัวเอง คุณจะต้องใช้ประเภทการแลกเปลี่ยนที่ไม่ใช่ทางตรง (หัวข้อ, หัว, fanout) เพื่อรับข้อความถึงคิวทั้งหมดในครั้งเดียว


1

ในขณะที่ฉันประเมินกรณีของคุณคือ:

  • ฉันมีคิวข้อความ (แหล่งที่มาของคุณสำหรับการรับข้อความให้ตั้งชื่อมันว่า q111)

  • ฉันมีผู้บริโภคหลายคนซึ่งฉันต้องการทำสิ่งต่าง ๆ ด้วยข้อความเดียวกัน

ปัญหาของคุณที่นี่คือในขณะที่ได้รับ 3 ข้อความโดยคิวนี้ข้อความ 1 ถูกใช้โดยผู้บริโภค A ผู้บริโภค B และ C อื่น ๆ ใช้ข้อความ 2 และ 3 ในกรณีที่คุณต้องการติดตั้งที่ Rabbitmq ส่งสำเนาเดียวกันของ ข้อความทั้งสามนี้ (1,2,3) ให้กับผู้ใช้ที่เชื่อมต่อทั้งสาม (A, B, C) พร้อมกัน

ในขณะที่การกำหนดค่าจำนวนมากสามารถทำได้เพื่อให้บรรลุสิ่งนี้วิธีง่ายๆคือการใช้แนวคิดสองขั้นตอนต่อไปนี้:

  • ใช้ dynamic Rabbitmq-shovel เพื่อรับข้อความจากคิวที่ต้องการ (q111) และเผยแพร่ไปยังการแลกเปลี่ยน fanout (แลกเปลี่ยนที่สร้างขึ้นเฉพาะสำหรับวัตถุประสงค์นี้เท่านั้น)
  • ตอนนี้กำหนดค่าผู้บริโภคของคุณใหม่ A, B & C (ผู้ที่กำลังฟังคิว (q111)) เพื่อฟังจากการแลกเปลี่ยน Fanout นี้โดยตรงโดยใช้คิวพิเศษและไม่ระบุชื่อสำหรับผู้บริโภคแต่ละราย

หมายเหตุ: ในขณะที่ใช้แนวคิดนี้จะไม่ใช้งานโดยตรงจากคิวแหล่งที่มา (q111) เนื่องจากข้อความที่ใช้ไปแล้วจะไม่ถูกนำไปแลกเปลี่ยน Fanout ของคุณ

หากคุณคิดว่าสิ่งนี้ไม่เป็นไปตามข้อกำหนดที่คุณต้องการ ... อย่าลังเลที่จะโพสต์คำแนะนำของคุณ :-)



0

ฉันคิดว่าคุณควรตรวจสอบการส่งข้อความของคุณโดยใช้โปรแกรมแลกเปลี่ยนfan-out ด้วยวิธีนี้คุณจะได้รับข้อความเดียวกันสำหรับผู้บริโภคที่แตกต่างกันภายใต้ตาราง RabbitMQ กำลังสร้างคิวที่แตกต่างกันสำหรับผู้บริโภค / สมาชิกรายใหม่แต่ละราย

นี่คือลิงค์สำหรับดูตัวอย่างการสอนใน javascript https://www.rabbitmq.com/tutorials/tutorial-one-javascript.html


-1

มีตัวเลือกหนึ่งที่น่าสนใจในสถานการณ์นี้ฉันไม่พบคำตอบที่นี่

คุณสามารถ Nack ข้อความด้วยคุณสมบัติ "requeue" ในคอนซูเมอร์รายหนึ่งเพื่อประมวลผลข้อความเหล่านั้นในอีกแอปหนึ่ง โดยทั่วไปการพูดมันไม่ถูกวิธี แต่อาจจะดีพอสำหรับใครบางคน

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

และระวังลูป (เมื่อผู้เจรจาต่อรองทุกคนขาดข้อความ + ข้อความที่ต้องชำระ)!


1
ฉันขอคำแนะนำอย่างสูงต่อเรื่องนี้เพราะมันไม่ได้ขยายขนาดด้วยวิธีการใด ๆ ไม่มีคำสั่งสำหรับผู้บริโภคคุณไม่สามารถรับประกันผู้บริโภค B ที่จะไม่ขอชำระได้รับข้อความก่อนที่ผู้บริโภค A ที่จะดำเนินการและตอบแทนลูกค้าลูปที่กล่าวถึงเป็นปัญหา อย่างที่คุณพูด "นี่เป็นการพูดโดยทั่วไปไม่ใช่วิธีที่ถูกต้อง" และฉันไม่สามารถนึกถึงสถานการณ์ที่สิ่งนี้จะดีกว่าคำตอบอื่น ๆ
Kevin Streicher
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.