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消息队列-消息任务分发与消息ACK确认机制(PHP版)
原文:(六)RabbitMQ消息队列-消息任务分发与消息ACK确认机制(PHP版) 在前面一章介绍了在PHP中如何使用RabbitMQ,至此入门的的部分就完成了,我们内心中一定还有很多疑问:如果多个消 ...
- (十四)RabbitMQ消息队列-启用SSL安全通讯
原文:(十四)RabbitMQ消息队列-启用SSL安全通讯 如果RabbitMQ服务在内网中,只有内网的应用连接,我们认为这些连接都是安全的,但是个别情况我们需要让RabbitMQ对外提供服务.这种情 ...
- (九)RabbitMQ消息队列-通过Headers模式分发消息
原文:(九)RabbitMQ消息队列-通过Headers模式分发消息 Headers类型的exchange使用的比较少,以至于官方文档貌似都没提到,它是忽略routingKey的一种路由方式.是使用H ...
- (八)RabbitMQ消息队列-通过Topic主题模式分发消息
原文:(八)RabbitMQ消息队列-通过Topic主题模式分发消息 前两章我们讲了RabbitMQ的direct模式和fanout模式,本章介绍topic主题模式的应用.如果对direct模式下通过 ...
随机推荐
- 在MFC对话框中添加状态栏
如果我们想实现在MFC对话框中添加状态栏显示,如何例如分状态栏为两列,第一列显示鼠标的当前位置,第二列显示当前的时间,(如上图). 1. 首先,打开在资源视图的String Table并添加两个ID: ...
- QQ的邮我功能
首先是从QQ邮箱中获取到“邮我”的功能获取代码,然后点击后就可以直接的设定好收件人为自己了. 给我写信
- angularjs ng-switch
<p> <a href="#" ng-click="toggle()">Toggle Section</a> </p& ...
- wince天气代码
#ifndef COMMON_H #define COMMON_H #include <Windows.h> typedef struct _operateInfo { HANDLE hS ...
- ADB shell出现error:device offline提示
解决办法: 1.adb kill-server 2.adb start-server 3.adb remount执行这3个命令然后重新键入adb shell应该就可以了
- 单线驱动74hc595(转)
源:http://blog.chinaunix.net/uid-10701701-id-91938.html 这个电路是国外一个哥们2009年的时候发表的. http://www.romanblack ...
- 洛谷 U4704 函数
设gcd(a,b)为a和b的最大公约数,xor(a,b)为a异或b的结果. 题目描述 kkk总是把gcd写成xor.今天数学考试恰好出到了gcd(a,b)=?这样的题目,但是kkk全部理解成了xor( ...
- Android利用canvas画各种图形 及Paint用法 .
引自:http://blog.csdn.net/carlfan/article/details/8139984 1.首先说一下canvas类: Class Overview The Canvas cl ...
- cc2530串口通信流程
//串口发送接收流程 main: //主函数 ->osal_init_system(); //操作系统初始化 ->osalInitTasks(); //任务初始化 -->ZDApp_ ...
- HDU 5176 The Experience of Love 带权并查集
The Experience of Love Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/O ...