RabbitMQ / AMQP : 단일 대기열, 동일한 메시지에 대한 여러 소비자?


146

나는 RabbitMQ와 AMQP를 일반적으로 사용하기 시작했습니다.

  • 메시지 대기열이 있습니다
  • 여러 소비자가 있는데 동일한 메시지로 다른 일을하고 싶습니다 .

RabbitMQ 문서의 대부분은 라운드 로빈에 중점을 둔 것으로 보입니다. 즉, 단일 메시지가 단일 소비자에 의해 소비되고 각 소비자간에로드가 분산되는 경우입니다. 이것은 실제로 내가 목격하는 행동입니다.

예 : 생산자에게 단일 대기열이 있고 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, 다른 터미널에는 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
주제가 잘 작동하고 나중에 더 많은 제어 기능을 제공하지만 실제로 팬 아웃을 원한다고 생각합니다.
— robthewolf

감사합니다 @UrbanEsc. 주제는 하나의 메시지가 여러 큐에 도달하여 문제를 해결하는 것으로 보이므로 각 큐 소비자가 소비합니다. 특정 사례에 대한 다중 대기열 / 단일 소비자 시나리오로 더 나아가게됩니다.
— mikemaccana

1
2018 년 (2016 년 및 이전 버전의 경우)의 답은 IMO Kafka와 같은 것을 사용하는 것입니다.
— WattsInABox 1

답변:


115

각 소비자에게 동일한 메시지를 받도록 할 수 있습니까? 즉, 두 소비자 모두 메시지 1, 2, 3, 4, 5, 6을 수신합니까? 이것을 AMQP / RabbitMQ에서 무엇이라고하나요? 일반적으로 어떻게 구성됩니까?

소비자가 동일한 대기열에있는 경우 아닙니다. RabbitMQ의 AMQP 개념 안내서에서 :

AMQP 0-9-1에서 메시지는 소비자간에로드 균형 조정됨을 이해해야합니다.

이것은 큐 내의 라운드 로빈 동작이 지정 되어 있으며 구성 할 수 없음을 의미합니다. 즉, 여러 소비자가 동일한 메시지 ID를 처리하려면 별도의 큐가 필요합니다.

이것은 일반적으로 수행됩니까? 교환기가 단일 소비자 대신 두 개의 별도 대기열로 메시지를 라우팅해야합니까?

아니요, 각 소비자가 동일한 메시지 ID를 처리하는 단일 대기열 / 여러 소비자는 불가능합니다. 교환기가 메시지를 두 개의 개별 대기열로 라우팅하는 것이 실제로 더 좋습니다.

너무 복잡한 라우팅이 필요하지 않기 때문에 팬 아웃 교환 이이를 잘 처리합니다. node-amqp에는 메시지를 직접 연결에 게시 할 수있는 '기본 교환'개념이 있기 때문에 Exchange에 너무 집중하지 않았지만 대부분의 AMQP 메시지는 특정 교환에 게시됩니다.

팬 아웃 교환은 다음과 같습니다. 송수신

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 Fair Dispatch
— Ommit

3
명확히하기 위해 : '기본 교환'은 node-amqp에 국한되지 않습니다. 메시지가 기본 교환에 게시되면 라우팅 키 (해당 메시지가 게시 된)는 AMQP 브로커에 의해 큐 이름으로 처리됩니다. 따라서 큐에 직접 게시 할 수있는 것 같습니다. 그러나 당신은 아닙니다. 브로커는 단순히 큐 이름과 동일한 라우팅 키를 사용하여 각 큐를 기본 교환에 바인드합니다.
— Ruslan Stelmachenko 2016 년

2
큐가 관련되지 않고 멀티 캐스팅이있는 rabbitmq의 Apache activemq jms 주제에 대한 대안이 있습니까?
— pantonis 2016 년

여러 사용자가 동일한 사용자로 로그인 한 경우 메시지는 하나의 장치 만 가져옵니다. 어떻게 해결하거나 아이디어를 얻을 수 있습니까?
— Rafiq

@Rafiq 당신은 이것에 대해 질문을해야합니다.
— mikemaccana

28

rabbitmq 튜토리얼을 읽으십시오 . 대기열이 아닌 교환을 위해 메시지를 게시합니다. 그런 다음 적절한 대기열로 라우팅됩니다. 귀하의 경우 각 소비자에 대해 별도의 큐를 바인딩해야합니다. 이렇게하면 메시지를 완전히 독립적으로 사용할 수 있습니다.


27

마지막 몇 가지 답변은 거의 정확합니다. 다른 소비자와 끝내야하는 프로세스를 매우 간단하게 해야하는 메시지를 생성하는 수많은 앱이 있습니다.

여러 소비자를 동일한 메시지에 표시하려면 다음 절차를 수행하십시오.

각 큐 특성에서 메시지를 수신 할 각 앱마다 하나씩 여러 개의 큐를 작성하여 amq.direct 교환과 라우팅 태그를 "바인드"하십시오. amq.direct로 보내도록 게시 앱을 변경하고 대기열이 아닌 라우팅 태그를 사용하십시오. 그런 다음 AMQP는 동일한 바인딩으로 각 큐에 메시지를 복사합니다. 매력처럼 작동합니다 :)

예 : 생성 한 JSON 문자열이 있다고 가정하고 라우팅 태그 "new-sales-order"를 사용하여 "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를 살펴보십시오 . com / tutorials / tutorial-five-python.html

다양한 방법으로 메시지를 라우팅합니다. 나는 그들이 파이썬과 자바를위한 것이라는 것을 알고 있지만 원리를 이해하고 당신이 무엇을하고 있는지 결정한 다음 JS에서 그것을하는 방법을 찾는 것이 좋습니다. 간단한 팬 아웃 ( 자습서 3 ) 을 수행하려는 것처럼 들리며 , 이는 교환기에 연결된 모든 큐에 메시지를 보냅니다.

수행중인 작업과 수행하려는 작업의 차이점은 기본적으로 팬 아웃을 설정 및 교환하거나 입력한다는 것입니다. 팬 아웃 예외는 연결된 모든 대기열에 모든 메시지를 보냅니다. 각 대기열에는 모든 메시지에 개별적으로 액세스 할 수있는 소비자가 있습니다.

그렇습니다. 이것은 일반적으로 이루어지며 AMPQ의 기능 중 하나입니다.


'이것은 일반적으로 수행됩니까?'를 제외하고는 훌륭한 대답입니다. 나는 '각 소비자가 동일한 메시지를 수신하게'하는 것을 언급하고있었습니다. 일반적으로 수행되지는 않습니다 (동일한 대기열의 소비자는 항상 라운드 로빈). 아마도 명확하지 않은 내 잘못 일 것입니다.
— mikemaccana

실제로 나는 그것이 당신이 그것을 사용하려는 것에 달려 있다고 말하려고합니다. pub / sub 또는 work queue의 두 가지 기본 선택 사항이 있습니다. 원래 설정은 작업 대기열 이었지만 원하는 것은 팬 아웃 펍 / 서브였습니다. 그들은 여기서 일반적인 사용법은 당신이하고 싶은 것에 전적으로 달려 있다고 지적합니다.
— robthewolf

물론 작업 대기열에서 동일한 메시지 (예 : 동일한 메시지 ID)는 다른 소비자가 처리하지 않습니다. 암시 적으로 라운드 로빈입니다. 다시 이것은 아마도 충분히 명확하지 않은 내 잘못 일 것입니다.
— mikemaccana

우리는 여기서 교차 목적으로 이야기하는 것 같습니다.
— robthewolf

혼란에 대해 죄송합니다. 동일한 대기열에있는 소비자가 동일한 메시지 ID를 처리하는 작업 대기열을 가질 수있는 방법이있는 경우 참조를 알려주십시오. 그렇지 않으면 나는 다른 곳에서 읽은 것을 계속 믿을 것입니다.
— mikemaccana


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

원하는 동작을 얻으려면 각 소비자가 자체 대기열에서 소비하도록하십시오. 모든 대기열에 메시지를 한 번에 가져 오려면 비 직접 교환 유형 (주제, 헤더, 팬 아웃)을 사용해야합니다.


1

귀하의 사례를 평가할 때 :

  • 메시지 대기열이 있습니다 (메시지 수신 소스, 이름을 q111로 지정하십시오)

  • 여러 소비자가 있는데 동일한 메시지로 다른 일을하고 싶습니다.

문제는이 대기열에서 3 개의 메시지를 수신하는 동안 메시지 1은 소비자 A에 의해 소비되고 다른 소비자 B 및 C는 메시지 2 및 3을 소비합니다. rabbitmq가 동일한 사본을 전달하는 설정이 필요한 경우 이 세 가지 메시지 (1,2,3)를 모두 연결된 세 소비자 (A, B, C)에게 동시에 보냅니다.

이를 위해 많은 구성을 수행 할 수 있지만 간단한 방법은 다음 두 단계 개념을 사용하는 것입니다.

  • 다이나믹 rabbitmq- 삽을 사용하여 원하는 대기열 (q111)에서 메시지를 픽업하고 팬 아웃 교환 (이 목적을 위해 독점적으로 생성 및 전용 된 교환)에 게시하십시오.
  • 이제 각 소비자에 대해 독점적이며 익명의 대기열을 사용하여이 팬 아웃 교환에서 직접들을 수 있도록 소비자 A, B & C (대기열 (q111)을 청취 한 소비자)를 재구성하십시오.

참고 :이 개념을 사용하는 동안 소스 큐 (q111)에서 직접 소비하지는 않습니다. 이미 소비 한 메시지는 팬 아웃 교환에 삽질되지 않기 때문입니다.

이것이 귀하의 정확한 요구 사항을 충족시키지 못한다고 생각되면 제안을 게시하십시오 :-)




-1

이 시나리오에는 내가 대답에서 찾지 못한 흥미로운 옵션이 있습니다.

한 소비자의 "요청"기능이있는 메시지를 Nack하여 다른 소비자에게 처리 할 수 ​​있습니다. 일반적으로 올바른 방법은 아니지만 누군가에게 충분할 것입니다.

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

그리고 루프를 조심하십시오 (모든 concumers가 nack + requeue 메시지를 보낼 때)!


1
어떤 식 으로든 확장 할 수 없으므로 이것에 대해 강력히 조언합니다. 소비자를위한 주문은 없으며, 소비자 B는 그것을 재 요청하지 않을 것이라고 보장 할 수 없으며, 소비자 A보다 먼저 메시지를 수신하여 처리하고 재발송합니다. 언급 된 루프는 문제입니다. "이것은 일반적으로 옳은 방법이 아닙니다"라고 말하면서 이것이 다른 답변보다 나은 시나리오를 생각할 수는 없습니다.
— Kevin Streicher
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.