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模式下通过 ...
随机推荐
- scala模式匹配与样例类
样本类:添加了case的类便是样本类.这种修饰符可以让Scala编译器自动为这个类添加一些语法上的便捷设定.如下: 1.添加与类名一致的工厂方法.也就是说,可以写成Var("x") ...
- 修改6S Fortran77 代码,建立查找表
逐像元大气校正,常预先计算查找表(LUT,LookUp Tabel),6S大气辐射传输模式也可以用来计算LUT.但6S源程序输出信息多,且浮点数输出精度低,不利于提取关键信息生成LUT,本文描述了 ...
- 优化之zencart第一时间修改原始内容
Zen Cart 基本修改指南 Zen Cart,全球顶级B2C商城网站!要想自行搭建一个基本的Zen Cart的网站,这篇文章是绝对不能错过的.目前我已经做了两个B2C网站,但是还是离不开这篇文章的 ...
- HDU 1890 Robotic Sort | Splay
Robotic Sort Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) [Pr ...
- python socket编程---从使用Python开发一个Socket示例说到开发者的思维和习惯问题
今天主要说的是一个开发者的思维和习惯问题. 思维包括编程的思维和解决一个具体问题的分析思维,分析思路,分析方法,甚至是分析工具. 无论是好习惯还是不好的习惯,都是在者一天一天的思维中形成的.那些不好的 ...
- JavaScript数组函数unshift、shift、pop、push使用实例
如何声明数组 s中数组的声明可以有几种方式声明 复制代码代码如下: var tmp = []; // 简写模式var tmp = new Array(); // 直接new一个var tmp = A ...
- 使用iptraf,ifstat查看网络流量
1. 使用 iptraf iptraf是一个实时查看网络流量的文本屏幕界面工具. 有些系统不一定安装了, 如果是 RHEL,那么就去找安装盘中的 iptraf*.rpm 包安装: 如果是 CentOS ...
- CF div2 D BFS
http://codeforces.com/contest/676/problem/D 题目大意: 勇者去迷宫杀恶龙.迷宫是有n*m的方格子组成的.迷宫上有各种记号,这些记号表达着能走的方向.当且仅当 ...
- navicat 导出mysql表结构
选中需要导出表结构的数据库,右键,在显示的菜单中选择“数据传输”这一项 ,在弹出窗口中“数据传输”单击选择“高级”一项,在“高级”中把“记录选项”中的勾去掉,在做一些设置,最后导出表数据就不会导出记录 ...
- 转 oracle 开发 第03章 sqlplus
目录 1.查看表结构 desc 2.编辑SQL语句 append.list.change.run 3.保存.检索并运行文件 save.get.start.edit.spool 4.格式化列 colum ...