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.