RabbitMQ学习小结(三)----交换机

前面都只使用了一个队列,消息是依次发送给绑定到该队列的接收端。每次消息都只会发送给其中一个接收端,如果需要将消息广播出去,让每个接收端都能收到,那么就要使用交换机。本篇演示了交换机的工作方式。

一、发布/订阅

1、上小节搭建工作队列,每个消息任务只能分发给一个工作者(消费者)。  分发一个消息给多个消费者。这中模式称为"发布/订

阅"。

2、构建简单的日志系统。第一个程序负责发送日志消息,第二个程序负责获取消息并输出内容。所有正在运行的接收方都会接收消

息。其中一个接收者把日志写入硬盘,另一个接收者把日志输出到屏幕上。 日志消息被广播给所有的接受者。

二、交换机

前面的教程中,我们发送消息到队列并从中取出消息。现在是时候介绍RabbitMQ中完整的消息模型了。

简单概括之前的教程:

发布者(producer):是发布消息的应用程序。

消息队列(queue):用于消息存储的缓冲。

消费者(consumer):是接收消息的应用程序。

1、RabbitMQ消息模型的核心理念:发布者是将消息直接发送给交换机,由交换机来决定消息是发送到哪个队列,或者是忽略消息。发布者(producer)只需要把消息发送给一个交换机(exchange)。交换机非常简单,它一边从发布者方接收消息,一边把消息推送到队列

2、交换机怎么处理消息?

交换机必须知道如何处理它接收到的消息,是应该推送到指定的队列还是是多个队列,或者是直接忽略消息。这些规则

是通过交换机类型(exchange type)来定义的,可以参见下图:

     RabbitMQ学习小结(三)----交换机_第1张图片

RabbitMQ有四种内置交换机类型:

直连交换机——direct   主题交换机——topic
头交换机——headers    扇形交换机——fanout

本节主要讨论扇形交换机。

创建一个fanout类型的交换机,命名为logs:

channel.exchange_declare(exchange='logs', type='fanout')
扇形交换机它把消息发送给它所知道的所有队列。这正是我们的日志系统所需要的。

3、交换器列表

RabbitMQ学习小结(三)----交换机_第2张图片

这个列表中有一些叫做amq.*的交换器。这些都是默认创建的,不过这时候你还不需要使用他们。

4、匿名交换机

使用rabbitmqctl list_exchanges,会发现一个无名(空字符串)的exchange,类型为direct。在RabbitMQ中,它是默认创建好的交换机(default exchange)。
本质上所有的消息发送都要送往exchange(可以没有队列,但不能没有交换机,没有队列时消息直接被丢弃)。
RabbitMQ提供了一种直接向Queue发送消息的快捷方法:直接使用未命名的exchange,不用绑定routing_key,直接用它指定队列名。

前面的教程中我们对交换机一无所知,但仍然能够发送消息到队列中。因为我们使用了命名为空字符串("")默认的交换机

如何发布一则消息: 

channel.basic_publish(exchange='',routing_key='hello',body=message)
 exchange参数就是交换机的名称。空字符串代表默认或者匿名交换机: 消息将会根据指定的routing_key分发到指定的队列

但是,现在我们可以发送消息到一个具名交换机了:

channel.basic_publish(exchange='logs', routing_key='', body=message)

三、临时队列

1、队列命名:给一个队列命名很重要---需要把工作者(workers)指向正确的队列。如果打算在发布者和消费者之间共享队列的话,给队列命名十分重要的。

2、我们打算接收所有的日志消息,而不仅仅是一小部分。关心的是最新的消息而不是旧的。

3、我们连接上RabbitMQ服务器的时候,我们需要一个全新的、空的队列。可以手动创建一个随机的队列名,或者服务器帮我们选择一个随机的队列名(推荐)。  

result = channel.queue_declare()
 可以通过result.method.queue获得已经生成的随机队列名。它可能是这样子的:amq.gen-U0srCoW8TsaXjNh73pnVAw==。

4、当与消费者(consumer)断开连接的时候,这个队列应当被立即删除。exclusive标识符即可达到此目的。

result = channel.queue_declare(exclusive=True)

四、绑定(Bindings)

  

我们已经创建了一个扇型交换机(fanout)和一个队列。现在我们需要告诉交换机如何发送消息给我们的队列。交换器和队列之间的联系我们称之为绑定(binding)。

channel.queue_bind(exchange='logs',queue=result.method.queue)
现在,logs交换器将会把消息添加到我们的队列中。
绑定列表: 

rabbitmqctl list_bindings  #列出所有现存的绑定
五、处理流程和代码

生产者
1、创建连接
2、创建交换机(扇形交换机,由交换机决定发送给哪个队列)
3、发送消息到指定交换机

消费者
1、创建连接和通道
2、创建交换机(与生产者交换机对应)
3、创建临时队列(每个消费者都要创建的独立的队列,用于接收所有消息)
4、创建绑定(将交换机与临时队列绑定)
5、在指定交换机接收消息并处理

代码:

RabbitMQ学习小结(三)----交换机_第3张图片

最重要的改变就是我们把消息发送给logs交换机而不是匿名交换机。在发送的时候我们需要提供routing_key参数,但是它的值会被扇型交换机(fanout exchange)忽略。

send.py:

#!/usr/bin/python
import pika
import sys
 
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

channel.exchange_declare(exchange='logs',type='fanout')
 
message = ' '.join(sys.argv[1:]) or "info: Hello World!"
channel.basic_publish(exchange='logs',routing_key='',body=message)                                                                 
print " [x] Sent %r" % (message,)
connection.close()
在连接成功之后,我们声明了一个交换器,这一个是很重要的,因为不允许发布消息到不存在的交换器。
如果没有绑定队列到交换器,消息将会丢失。但这个没有所谓,如果没有消费者监听,那么消息就会被忽略。

receive.py:

#!/usr/bin/env python
import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

channel.exchange_declare(exchange='logs',type='fanout')

result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue

channel.queue_bind(exchange='logs',queue=queue_name)

print ' [*] Waiting for logs. To exit press CTRL+C'

def callback(ch, method, properties, body):
    print " [x] %r" % (body,)

channel.basic_consume(callback,queue=queue_name,no_ack=True)
channel.start_consuming()

这样我们就完成了。如果你想把日志保存到文件中,只需要打开控制台输入:

python receive.py > logs_from_rabbit.log
如果你想在屏幕中查看日志,那么打开一个新的终端然后运行:
python receive.py
当然发送日志:

python send.py
使用rabbitmqctl list_bindings你可确认已经创建的队列绑定。你可以看到运行中的两个receive.py程序:

显示结果很直观:logs交换器把数据发送给两个系统命名的队列。这就是我们所期望的。

注意:
(1)如果交换机还没有绑定队列,即消费者还没有运行,而先运行了生产者,那交换机就自动把消息丢弃了
(2)上一节提到了队列和消息可以持久化,有兴趣的不妨试一下交换机的持久化

交换机部分先告一段落!!!回去睡觉。

你可能感兴趣的:(openstack组件研究)