场景
- 一个生产者和多个消费者
- 生产的消息会同时发送到所有的消费者(广播)
示意图如下:
在下面的例程中,生产者发送日志数据给所有的消费者,所有的消费者均接收到所有的日志消息。
生产者
代码如下:
# -*- coding: utf-8 -*-
import sys
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
# 创建Exchange
channel.exchange_declare(exchange='logs', exchange_type='fanout')
message = ' '.join(sys.argv[1:]) or "Hello World!"
channel.basic_publish(exchange='logs', routing_key='', body=message)
print("[x] Sent to %r" %message)
connection.close()
详细说明:
- 连接、通道请参考之前的博文
- 创建Exchange。之前的例程中都使用了默认的Exchange,此处创建了一个名为logs,类型为 fanout的Exchange
- 生产者发送的所有消息均不是直接到queue,而是到Exchange,由Exchange根据规则确定消息走向,可以追加到一个队列、多个队列或者丢弃
- Exchange的类型决定消息转发规则,常用的有4类:direct, topic, headers 和 fanout。详细说明请参考RabbitMq入门简介
- 此处使用fanout,它会广播所有的消息到所有的队列。使用该类型时,不必指定routing_key,因为它的值会被忽略
- 发送消息到该Exchange。不能向未创建的Exchange发送消息。
消费者
# -*- coding: utf-8 -*-
import pika
import time
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='logs', exchange_type='fanout')
result = channel.queue_declare(queue='', exclusive=True)
queue_name = result.method.queue
channel.queue_bind(exchange='logs', queue=queue_name)
def callback(ch, method, properties, body):
print(" [x] Received %r" % body)
# 不发确认包
channel.basic_consume(queue=queue_name, on_message_callback=callback, auto_ack=True)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()
详细说明:
- 创建队列。之前的例程中,创建队列时都会有一个名字。命名队列对于需要在生产者和消费者之间共享该队列时是必须的。
- 对于订阅者来说,队列的名字不重要了,因为要从所有的队列中接收消息
- 使用空字符串会使用随机的队列名字,看起来类似于
amq.gen-Q48wnVe5JFf7BGlgeTWPWA
- 当连接断开时,队列应该自动删除,所以使用了
exclusive=True
属性
- 绑定。绑定的是队列和Exchange。使得队列能够接收到来自Exchange的消息,所以需要把它们两者绑定起来。
- 如果没有队列绑定到Exchange,生产者发送到该Exchange的消息会被丢弃
- 不需要确认。消费者只关心最新的消息,不需要关心已经丢失的旧消息。
结果
可以启动任意多个消费者,当生产者启动后,所有的消费者都收到了相同的消息。