上篇文章中,我们把每个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')
channel.exchange_declare(exchange='logs',
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.
$ 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了:

  1. channel.basic_publish(exchange='logs',
  2. routing_key='',
  3. body=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会随机为我们选择这个名字。方法:

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

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

  1. result = channel.queue_declare(exclusive=True)
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)
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:

  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()
#!/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:

  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()
#!/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:

  1. $ python receive_logs.py > logs_from_rabbit.log
$ python receive_logs.py > logs_from_rabbit.log

Consumer2:打印到屏幕:

  1. $ python receive_logs.py
$ python receive_logs.py

接下来,Producer:

  1. $ python emit_log.py
$ 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.
$ sudo rabbitmqctl list_bindings
Listing bindings ...
logs exchange amq.gen-JzTY20BRgKO-HjmUJj0wLg queue []
logs exchange amq.gen-vso0PVvyiRIL2WoV3i48Yg queue []
...done.

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

参考资料:

1. http://www.rabbitmq.com/tutorials/tutorial-three-python.html

RabbitMQ 分发到多Consumer(Publish/Subscribe)的更多相关文章

  1. RabbitMQ入门(3)——发布/订阅(Publish/Subscribe)

    在上一篇RabbitMQ入门(2)--工作队列中,有一个默认的前提:每个任务都只发送到一个工作人员.这一篇将介绍发送一个消息到多个消费者.这种模式称为发布/订阅(Publish/Subscribe). ...

  2. 【译】RabbitMQ:发布-订阅(Publish/Subscribe)

    在前一篇教程中,我们创建了一个工作队列,我们假设在工作队列后的每一个任务都只被调度给一个消费者.在这一部分,我们将做一些完全不一样的事情,调度同一条消息给多个消费者,也就是有名的“发布-订阅”模式.为 ...

  3. 四.RabbitMQ之发布/订阅(Publish/Subscribe)

    一.基础知识点 在上述章节中,我们理解的RabbitMQ是基于如下这种模式运作的. 而事实上,这只是我们简单化了的模型的结果,真正的模型应该是这样的. P:Producer 生产者,生产消息,把它放进 ...

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

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

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

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

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

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

  7. 【RabbitMQ】Publish/Subscribe

    Publish/Subscribe 在上一节我们创建了一个work queue.背后的设想为每个任务被分发给明确的消费者.这节内容我们将做一些完全不同的事情 -- 我们将发送一条消息给多个消费者.这种 ...

  8. Part1.2 、RabbitMQ -- Publish/Subscribe 【发布和订阅】

    python 目录 (一).交换 (Exchanges) -- 1.1 武sir 经典 Exchanges 案例展示. (二).临时队列( Temporary queues ) (三).绑定(Bind ...

  9. RabbitMQ学习第三记:发布/订阅模式(Publish/Subscribe)

    工作队列模式是直接在生产者与消费者里声明好一个队列,这种情况下消息只会对应同类型的消费者. 举个用户注册的列子:用户在注册完后一般都会发送消息通知用户注册成功(失败).如果在一个系统中,用户注册信息有 ...

随机推荐

  1. css3+html5特效-向上滑动

    css+html5特效-向上滑动 效果描述:切换的下拉和上拉状态 鼠标悬浮:下拉鼠标离开:上拉 /*外容器设置*/ .box1{position:relative;top:100px;left:100 ...

  2. 转载:让Windows Server 2012r2 IIS8 ASP.NET 支持10万并发请求

    由于之前使用的是默认配置,服务器最多只能处理5000个同时请求,今天下午由于某种情况造成同时请求超过5000,从而出现了上面的错误. 为了避免这样的错误,我们根据相关文档调整了设置,让服务器从设置上支 ...

  3. 大数据实操3 - hadoop集群添加新节点

    hadoop集群支持动态扩展,不需要停止原有集群节点就可以实现新节点的加入. 我是使用docker搭建的进群环境,制作了镜像文件,这里以我的工作基础为例子介绍集群中添加集群的方法 一.制作一个新节点 ...

  4. [Java]刷题中的Java基础

    数组 取数组长度是属性length ArrayList ArrayList<Integer> maxRootHeap = new ArrayList<Integer>(); m ...

  5. 导出Excel工具类

    import java.io.OutputStream; import java.lang.reflect.Method; import java.text.SimpleDateFormat; imp ...

  6. mysql 聚集索引和非聚集索引问题(整理)

    mysql的聚集索引和非聚集索引 前几天做了一个面试,从优化数据库谈到索引,最后问了我聚集索引和非聚集索引的问题.当时那个叫悔恨啊,平时学习mysql索引这方便的知识,也都看到了这样的字眼,,但总的来 ...

  7. 谈谈逆向android里面的so

    1. 加密sgf算法分析 2.gnugo瘦身记

  8. oracle异机恢复 open resetlogs 报:ORA-00392

    参考文档:ALTER DATABASE OPEN RESETLOGS fails with ORA-00392 (Doc ID 1352133.1) 打开一个克隆数据库报以下错误: SQL> a ...

  9. 手绘raft算法

    手绘raft算法 互联网技术窝 2019-04-07 12:06:05 在现实的分布式系统中,不能可能保证集群中的每一台机器都是100%可用可靠的,集群中的任何机器都可能发生宕机.网络连接等问题导致集 ...

  10. Solved: RDP Disconnected – Error Code 2825 mremote