[英]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.