簡體   English   中英

ActiveMQ:單生產者,多消費者

[英]ActiveMQ: Single producer, multiple consumers

我有一個使用 ActiveMQ 的消息隊列。 Web 請求將消息放入持久性 = True 的隊列中。 現在,我有 2 個消費者,它們都作為單獨的會話連接到這個隊列。 消費者 1 總是確認該消息,但消費者 2 從不確認。

現在,我閱讀了這個http://activemq.apache.org/how-does-a-queue-compare-to-a-topic.html ,其中指出:

JMS 隊列實現負載均衡器語義。 一條消息只會被一個消費者接收。 如果在發送消息時沒有可用的消費者,它將被保留,直到可以處理消息的消費者可用。 如果消費者收到一條消息並且在關閉之前沒有確認它,那么該消息將被重新傳遞給另一個消費者。 一個隊列可以有許多消費者,消息在可用消費者之間進行負載平衡。

我從中了解到的是,我希望所有消息最終都由消費者 1 處理,因為它總是承認。 由於消費者 2 沒有確認,消息應該被發送給消費者 1。

但我注意到以下幾點: 1. 當我提交一個請求時,我只看到消費者 1 的每第二個請求。另一個請求沒有出現,它存儲在 ActiveMQ 中。 我想它去了不承認的消費者 2。 那么接下來應該是消費者 1 嗎?

我只需要確保消息只由一個消費者處理。 在我的例子中,這個消費者是一個國家(站點)X 的機器。每條消息只需要在一個國家(機器)中處理。 但是所有國家(機器)都應該收到消息。 如果消息中的國家 ID 匹配,它將確認。 所以只會發送 1 個確認/消息。

我接收/處理消息的代碼如下所示:

# --------------------------------------------- MODULE IMPORT ---------------------------------------------------------#
import argparse
import json
import logging
import multiprocessing as mp
import sys

import stomp
from tvpv_portal.services.msgbkr import MsgBkr
from utils import util


# --------------------------------------------- DEVELOPMENT CODE ------------------------------------------------------#
log = logging.getLogger(__name__)


class MessageProcessingListener(stomp.ConnectionListener):
    """This class is responsible for processing (consuming) the messages from ActiveMQ."""

    def __init__(self, conn, cb):
        """Initialization.

        Args:
            conn -- Connection object
            cb   -- Callback function
        """

        self._conn = conn
        self._cb = cb

    def on_error(self, headers, body):
        """When we get an error.

        Args:
            headers -- Message header
            body    -- Message body
        """

        log.error('Received error=%s', body)

    def on_message(self, headers, body):
        """When we receive a message.

        Args:
            headers -- Message header
            body    -- Message body
        """

        log.info('Received message')

        # Deserialize the message.
        item = json.loads(body)

        import pprint
        pprint.pprint(item)

        # TODO: check if msg is to be handled by this SITE. If so, acknowledge and queue it. Otherwise, ignore.

        # Put message into queue via callback (queue.put) function.
        #self._cb(item)

        # TODO: we only send acknowledge if we are supposed to process this message.
        # Send acknowledgement to ActiveMQ indicating message is consumed.
        self._conn.ack(headers['message-id'], headers['subscription'])


def worker(q):
    """Worker to retrieve item from queue and process it.

    Args:
        q -- Queue
    """

    # Run in an infinite loop. Get an item from the queue to process it. We MUST call q.task_done() to indicate
    # that item is processed to prevent deadlock.
    while True:
        try:
            item = q.get()

            # TODO: We will call external script from here to run on Netbatch in the future.
            log.info('Processed message')

        finally:
            q.task_done()


def flash_mq_rst_handler_main():
    """Main entry to the request handler."""

    # Define arguments.
    parser = argparse.ArgumentParser(description='Flash message queue request handler script',
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter,
                                     add_help=False)

    opts = parser.add_argument_group('Options')
    opts.add_argument('-h', '--help', action='help',
                      help='Show this help message and exit')
    opts.add_argument('--workers', metavar='val', type=int, default=4,
                      help='Number of worker processes')
    opts.add_argument('--log', metavar='file', type=util.get_resolved_abspath, default='flash_mq_rst_handler.log',
                      help='Log file')

    # Parse arguments.
    args = parser.parse_args()

    # Setup logger.
    util.configure_logger(args.log)
    log.info('Command line %s', ' '.join(map(str, sys.argv)))

    # Create a managed queue to store messages retrieved from message queue.
    queue = mp.Manager().JoinableQueue()

    # Instantiate consumer message broker + ensure connection.
    consumer = MsgBkr(producer=False)
    if not consumer.is_connected():
        log.critical('Unable to connect to message queue; please debug')
        sys.exit(1)

    # Register listener with consumer + queue.put as the callback function to trigger when a message is received.
    consumer.set_listener('message_processing_listener', MessageProcessingListener, cb=queue.put)

    # Run in an infinite loop to wait form messages.
    try:
        log.info('Create pool with worker=%d to process messages', args.workers)
        with mp.Pool(processes=args.workers) as pool:
            p = pool.apply_async(worker, (queue,))
            p.get()
    except KeyboardInterrupt:
        pass

    # See MsgBkr. It will close the connection during exit() so we don't have to do it.
    sys.exit(0)


if __name__ == '__main__':
    flash_mq_rst_handler_main()

JMS 橋解決了這個問題: https : //activemq.apache.org/components/artemis/documentation/1.1.0/jms-bridge.html

能夠讓 IT 配置創建 N+1 隊列。 源(傳入)隊列是放置所有消息的地方。 基於消息中的某些選擇器(如標頭中的 'some_key': 'some_value'),消息可以路由到 N 個目的地(傳出)隊列之一。 然后,每個站點都可以偵聽特定的消息隊列。 同一目標隊列上的多個消費者將以循環方式獲取消息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM