(转)RabbitMQ消息队列(四):分发到多Consumer(Publish/Subscribe)
上篇文章中,我们把每个Message都是deliver到某个Consumer。在这篇文章中,我们将会将同一个Message deliver到多个Consumer中。这个模式也被成为 "publish / subscribe"。
这篇文章中,我们将创建一个日志系统,它包含两个部分:第一个部分是发出log(Producer),第二个部分接收到并打印(Consumer)。 我们将构建两个Consumer,第一个将log写到物理磁盘上;第二个将log输出的屏幕。
1. Exchanges
关于exchange的概念在《RabbitMQ消息队列(一): Detailed Introduction 详细介绍》中有详细介绍。现在做一下简单的回顾。
RabbitMQ 的Messaging Model就是Producer并不会直接发送Message到queue。实际上,Producer并不知道它发送的Message是否已经到达queue。
Producer发送的Message实际上是发到了Exchange中。它的功能也很简单:从Producer接收Message,然后投递到queue中。Exchange需要知道如何处理Message,是把它放到那个queue中,还是放到多个queue中?这个rule是通过Exchange 的类型定义的。

我们知道有三种类型的Exchange:direct, topic 和fanout。fanout就是广播模式,会将所有的Message都放到它所知道的queue中。创建一个名字为logs,类型为fanout的Exchange:
- channel.exchange_declare(exchange='logs',
- type='fanout')
Listing exchanges
通过rabbitmqctl可以列出当前所有的Exchange:
- $ sudo rabbitmqctl list_exchanges
- Listing exchanges ...
- logs fanout
- amq.direct direct
- amq.topic topic
- amq.fanout fanout
- amq.headers headers
- ...done.
注意 amq.* exchanges 和the default (unnamed)exchange是RabbitMQ默认创建的。
现在我们可以通过exchange,而不是routing_key来publish Message了:
- channel.basic_publish(exchange='logs',
- routing_key='',
- body=message)
2. Temporary queues
截至现在,我们用的queue都是有名字的:第一个是hello,第二个是task_queue。使用有名字的queue,使得在Producer和Consumer之前共享queue成为可能。
但是对于我们将要构建的日志系统,并不需要有名字的queue。我们希望得到所有的log,而不是它们中间的一部分。而且我们只对当前的log感兴趣。为了实现这个目标,我们需要两件事情:
1) 每当Consumer连接时,我们需要一个新的,空的queue。因为我们不对老的log感兴趣。幸运的是,如果在声明queue时不指定名字,那么RabbitMQ会随机为我们选择这个名字。方法:
- result = channel.queue_declare()
通过result.method.queue 可以取得queue的名字。基本上都是这个样子:amq.gen-JzTY20BRgKO-HjmUJj0wLg。
2)当Consumer关闭连接时,这个queue要被deleted。可以加个exclusive的参数。方法:
- result = channel.queue_declare(exclusive=True)
3. Bindings绑定

方法:
- channel.queue_bind(exchange='logs',
- queue=result.method.queue)
现在logs的exchange就将它的Message附加到我们创建的queue了。
Listing bindings
使用命令rabbitmqctl list_bindings。
4. 最终版本
我们最终实现的数据流图如下:

Producer,在这里就是产生log的program,基本上和前几个都差不多。最主要的区别就是publish通过了exchange而不是routing_key。
emit_log.py script:
- #!/usr/bin/env 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()
还有一点要注意的是我们声明了exchange。publish到一个不存在的exchange是被禁止的。如果没有queue bindings exchange的话,log是被丢弃的。
Consumer:receive_logs.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()
我们开始不是说需要两个Consumer吗?一个负责记录到文件;一个负责打印到屏幕?
其实用重定向就可以了,当然你想修改callback自己写文件也行。我们使用重定向的方法:
We're done. If you want to save logs to a file, just open a console and type:
- $ python receive_logs.py > logs_from_rabbit.log
Consumer2:打印到屏幕:
- $ python receive_logs.py
接下来,Producer:
- $ python emit_log.py
使用命令rabbitmqctl list_bindings你可以看我们创建的queue。
一个output:
- $ sudo rabbitmqctl list_bindings
- Listing bindings ...
- logs exchange amq.gen-JzTY20BRgKO-HjmUJj0wLg queue []
- logs exchange amq.gen-vso0PVvyiRIL2WoV3i48Yg queue []
- ...done.
这个结果还是很好理解的。
(转)RabbitMQ消息队列(四):分发到多Consumer(Publish/Subscribe)的更多相关文章
- RabbitMQ消息队列(四):分发到多Consumer(Publish/Subscribe)
上篇文章中,我们把每个Message都是deliver到某个Consumer.在这篇文章中,我们将会将同一个Message deliver到多个Consumer中.这个模式也被成为 "pub ...
- RabbitMQ消息队列(四):分发到多Consumer(Publish/Subscribe)[转]
上篇文章中,我们把每个Message都是deliver(提供)到某个Consumer.在这篇文章中,我们将会将同一个Message deliver(提供)到多个Consumer中.这个模式也被成为 & ...
- RabbitMQ消息队列(六):使用主题进行消息分发[转]
在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity(严重级别)的log.但是,这也是它之所以叫做简单日志 ...
- (转)RabbitMQ消息队列(六):使用主题进行消息分发
在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity的log.但是,这也是它之所以叫做简单日志系统的原因, ...
- RabbitMQ消息队列(六):使用主题进行消息分发
在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity的log.但是,这也是它之所以叫做简单日志系统的原因, ...
- (十四)RabbitMQ消息队列-启用SSL安全通讯
原文:(十四)RabbitMQ消息队列-启用SSL安全通讯 如果RabbitMQ服务在内网中,只有内网的应用连接,我们认为这些连接都是安全的,但是个别情况我们需要让RabbitMQ对外提供服务.这种情 ...
- (九)RabbitMQ消息队列-通过Headers模式分发消息
原文:(九)RabbitMQ消息队列-通过Headers模式分发消息 Headers类型的exchange使用的比较少,以至于官方文档貌似都没提到,它是忽略routingKey的一种路由方式.是使用H ...
- (八)RabbitMQ消息队列-通过Topic主题模式分发消息
原文:(八)RabbitMQ消息队列-通过Topic主题模式分发消息 前两章我们讲了RabbitMQ的direct模式和fanout模式,本章介绍topic主题模式的应用.如果对direct模式下通过 ...
- (六)RabbitMQ消息队列-消息任务分发与消息ACK确认机制(PHP版)
原文:(六)RabbitMQ消息队列-消息任务分发与消息ACK确认机制(PHP版) 在前面一章介绍了在PHP中如何使用RabbitMQ,至此入门的的部分就完成了,我们内心中一定还有很多疑问:如果多个消 ...
随机推荐
- android设置动态壁纸 (Wallpaper) 介绍
当进入改壁纸的设置页面 但是还没有设置时 09-21 07:55:05.575: INFO/System.out(1337): service onCreate09-21 07:55:05.614: ...
- 【JavaScript】停不下来的前端,自动化流程
http://kb.cnblogs.com/page/501270/ 流程 关于流程,是从项目启动到发布的过程.在前端通常我们都做些什么? 切图,即从设计稿中获取需要的素材,并不是所有前端开发都被要求 ...
- TP复习16
'APP_GROUP_LIST' => 'Home,Admin', //项目分组设定'DEFAULT_GROUP' => 'Home', //默认分组 开分组复制上面 AjaxReturn
- 解决ntfs格式的移动硬盘mount到Linux下时变成只读文件系统的问题
环境介绍 主机: XP 虚拟机:VirtualBox+Debian6 遇到的问题 我有一块西部数据的移动硬盘,虚拟机中有一些资料需要拷贝到移动硬盘中,然后我将移动硬盘插上后,执行如下设置,将移动硬盘分 ...
- Android 在onActivityResult()中设置图片setImageResource(resId) 或者改变view属性,不成功的解决办法
如果试验过的朋友就会发现,在onActivityResult()中设置这些属性,好像都不工作,虽然我死磕一番还是不知道具体原因,我直接默认它可能就是不能在里面设置,所以就只能在其他地方设置,幸好发现A ...
- SQL操作XML
前面一段时间为了赶项目的进度,一直在加班,现在空闲了下来将前面碰到的问题整理了一下,发现了一些十分有用的,在此记录下来,看能不能帮助到遇到同样问题的朋友,此文仅是自己个人的意见,若存在问题,还望不宁赐 ...
- String、Brush、Color 相互转换
1.String转换成Color Color color = (Color)ColorConverter.ConvertFromString(string); 2.String转换成Brush Bru ...
- 创建对象_原型(Prototype)模式_深拷贝
举例: 刚给我的客厅做了装修,朋友也希望给他的客厅做装修,他可能会把我家的装修方案拿过来改改就成,我的装修方案就是原型. 定义: 使用原型实例指定将要创建的对象类型,通过复制这 ...
- [Java] 内部类总结
内部类是指在一个外部类的内部再定义一个类.内部类作为外部类的一个成员,并且依附于外部类而存在的.内部类可为静态,可用protected和private修饰(而外部类只能使用public和缺省的包访问权 ...
- 标签云 - jqcloud 用法
html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...