上篇文章中,我们把每个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:

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

Listing exchanges

通过rabbitmqctl可以列出当前所有的Exchange:

  1. $ sudo rabbitmqctl list_exchanges
  2. Listing exchanges ...
  3. logs      fanout
  4. amq.direct      direct
  5. amq.topic       topic
  6. amq.fanout      fanout
  7. amq.headers     headers
  8. ...done.

注意 amq.* exchanges 和the default (unnamed)exchange是RabbitMQ默认创建的。

现在我们可以通过exchange,而不是routing_key来publish Message了:

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

2. Temporary queues

截至现在,我们用的queue都是有名字的:第一个是hello,第二个是task_queue。使用有名字的queue,使得在Producer和Consumer之前共享queue成为可能。

但是对于我们将要构建的日志系统,并不需要有名字的queue。我们希望得到所有的log,而不是它们中间的一部分。而且我们只对当前的log感兴趣。为了实现这个目标,我们需要两件事情:
    1) 每当Consumer连接时,我们需要一个新的,空的queue。因为我们不对老的log感兴趣。幸运的是,如果在声明queue时不指定名字,那么RabbitMQ会随机为我们选择这个名字。方法:

  1. result = channel.queue_declare()

通过result.method.queue 可以取得queue的名字。基本上都是这个样子:amq.gen-JzTY20BRgKO-HjmUJj0wLg。
    2)当Consumer关闭连接时,这个queue要被deleted。可以加个exclusive的参数。方法:

  1. result = channel.queue_declare(exclusive=True)

3. Bindings绑定

现在我们已经创建了fanout类型的exchange和没有名字的queue(实际上是RabbitMQ帮我们取了名字)。那exchange怎么样知道它的Message发送到哪个queue呢?答案就是通过bindings:绑定。

方法:

  1. channel.queue_bind(exchange='logs',
  2. 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:

  1. #!/usr/bin/env python
  2. import pika
  3. import sys
  4. connection = pika.BlockingConnection(pika.ConnectionParameters(
  5. host='localhost'))
  6. channel = connection.channel()
  7. channel.exchange_declare(exchange='logs',
  8. type='fanout')
  9. message = ' '.join(sys.argv[1:]) or "info: Hello World!"
  10. channel.basic_publish(exchange='logs',
  11. routing_key='',
  12. body=message)
  13. print " [x] Sent %r" % (message,)
  14. connection.close()

还有一点要注意的是我们声明了exchange。publish到一个不存在的exchange是被禁止的。如果没有queue bindings exchange的话,log是被丢弃的。
Consumer:receive_logs.py:

  1. #!/usr/bin/env python
  2. import pika
  3. connection = pika.BlockingConnection(pika.ConnectionParameters(
  4. host='localhost'))
  5. channel = connection.channel()
  6. channel.exchange_declare(exchange='logs',
  7. type='fanout')
  8. result = channel.queue_declare(exclusive=True)
  9. queue_name = result.method.queue
  10. channel.queue_bind(exchange='logs',
  11. queue=queue_name)
  12. print ' [*] Waiting for logs. To exit press CTRL+C'
  13. def callback(ch, method, properties, body):
  14. print " [x] %r" % (body,)
  15. channel.basic_consume(callback,
  16. queue=queue_name,
  17. no_ack=True)
  18. channel.start_consuming()

我们开始不是说需要两个Consumer吗?一个负责记录到文件;一个负责打印到屏幕?
其实用重定向就可以了,当然你想修改callback自己写文件也行。我们使用重定向的方法:
We're done. If you want to save logs to a file, just open a console and type:

  1. $ python receive_logs.py > logs_from_rabbit.log

Consumer2:打印到屏幕:

  1. $ python receive_logs.py

接下来,Producer:

  1. $ python emit_log.py

使用命令rabbitmqctl list_bindings你可以看我们创建的queue。
一个output:

  1. $ sudo rabbitmqctl list_bindings
  2. Listing bindings ...
  3. logs    exchange        amq.gen-JzTY20BRgKO-HjmUJj0wLg  queue           []
  4. logs    exchange        amq.gen-vso0PVvyiRIL2WoV3i48Yg  queue           []
  5. ...done.

这个结果还是很好理解的。

(转)RabbitMQ消息队列(四):分发到多Consumer(Publish/Subscribe)的更多相关文章

  1. RabbitMQ消息队列(四):分发到多Consumer(Publish/Subscribe)

    上篇文章中,我们把每个Message都是deliver到某个Consumer.在这篇文章中,我们将会将同一个Message deliver到多个Consumer中.这个模式也被成为 "pub ...

  2. RabbitMQ消息队列(四):分发到多Consumer(Publish/Subscribe)[转]

    上篇文章中,我们把每个Message都是deliver(提供)到某个Consumer.在这篇文章中,我们将会将同一个Message deliver(提供)到多个Consumer中.这个模式也被成为 & ...

  3. RabbitMQ消息队列(六):使用主题进行消息分发[转]

    在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity(严重级别)的log.但是,这也是它之所以叫做简单日志 ...

  4. (转)RabbitMQ消息队列(六):使用主题进行消息分发

    在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity的log.但是,这也是它之所以叫做简单日志系统的原因, ...

  5. RabbitMQ消息队列(六):使用主题进行消息分发

    在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity的log.但是,这也是它之所以叫做简单日志系统的原因, ...

  6. (十四)RabbitMQ消息队列-启用SSL安全通讯

    原文:(十四)RabbitMQ消息队列-启用SSL安全通讯 如果RabbitMQ服务在内网中,只有内网的应用连接,我们认为这些连接都是安全的,但是个别情况我们需要让RabbitMQ对外提供服务.这种情 ...

  7. (九)RabbitMQ消息队列-通过Headers模式分发消息

    原文:(九)RabbitMQ消息队列-通过Headers模式分发消息 Headers类型的exchange使用的比较少,以至于官方文档貌似都没提到,它是忽略routingKey的一种路由方式.是使用H ...

  8. (八)RabbitMQ消息队列-通过Topic主题模式分发消息

    原文:(八)RabbitMQ消息队列-通过Topic主题模式分发消息 前两章我们讲了RabbitMQ的direct模式和fanout模式,本章介绍topic主题模式的应用.如果对direct模式下通过 ...

  9. (六)RabbitMQ消息队列-消息任务分发与消息ACK确认机制(PHP版)

    原文:(六)RabbitMQ消息队列-消息任务分发与消息ACK确认机制(PHP版) 在前面一章介绍了在PHP中如何使用RabbitMQ,至此入门的的部分就完成了,我们内心中一定还有很多疑问:如果多个消 ...

随机推荐

  1. XianBicycle

    https://github.com/xialinchong/secrettalkandroid https://github.com/talentprince/PhotoView https://g ...

  2. asp.net解决:当前上下文中不存在名称“Session”

    第一种方法:将使用session的类页面继承 System.Web.UI.Page类,方法:public class AddUser : System.Web.UI.Page 第二种方法:在page里 ...

  3. [Node.js] Broswerify -- 1

    Browserify is a tool that brings node.js style development to the browser. The thing you can see on ...

  4. [Express] Level 1: First Step

    Installing Express Let's start building our new Express application by installing Express. Type the ...

  5. Swift 编程风格指南(raywenderlich.com 版本号)

    官方 raywenderlich.com Swift 编程风格指南 本文版权归 raywenderlich.com .The Official raywenderlich.com Swift Styl ...

  6. Redis 分区

    分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集. 分区的优势 通过利用多台计算机内存的和值,允许我们构造更大的数据库. 通过多核和多台计算机,允许我们扩展计算能力:通 ...

  7. SquashFs工具制作

    下面是SquashFs.tar.gz的下载地址,解压后,直接执行其中的Makefile即可. SquashFs.tar.gz 说明: 制作squashfs格式的压缩镜像: ./mksquashfs . ...

  8. Nice是如何做iOS客户端架构的?

    一个创业产品的iOS客户端架构到底怎么做呢?现下最有活力的图片社交软件Nice的技术负责人刘诗彬将为我们解答创业产品如何实现iOS客户端架构. 分享人:刘诗彬,毕业于北京邮电大学电子信息科学与技术专业 ...

  9. YYCache 设计思路

    iOS 开发中总会用到各种缓存,最初我是用的一些开源的缓存库,但到总觉得缺少某些功能,或某些 API 设计的不够好用.YYCache (https://github.com/ibireme/YYCac ...

  10. [经典算法] 蒙地卡罗法求 PI

    题目说明: 蒙地卡罗为摩洛哥王国之首都,该国位于法国与义大利国境,以赌博闻名.蒙地卡罗的基本原理为以乱数配合面积公式来进行解题,这种以机率来解题的方式带有赌博的意味,虽然在精确度上有所疑虑,但其解题的 ...