在之前的文章RabbitMQ入门(二)工作队列中,我们创建了一个工作队列。工作队列背后的假设是每一项任务都被准确地传送至一个worker。在本文中,我们将会做一些不同的事情——我们将会把一个消息发送至许多消费者中。这种模式被称为订阅模式(publish/subscribe)

  为了解释这种模式,我们将会构建一个简单的日志系统。它包含两个程序——第一个将会产生消息,第二个将会接收并输出这些消息。

  在我们的日志系统中,每一个正在运行的接收程序都会收到消息。在这种方式下,我们可以运行一个接收程序来接收并将日志保存至硬盘;同时,我们还能运行另一个接收程序,在屏幕上观察到日志的输出。

  特别地,发送的这些消息都会被广播到所有的接收程序。

交换(Exchanges)

  在之前的文章中,我们向队列发送消息,从队列中接受消息。现在是时候介绍RabbitMQ中的全部消息转发模式。

  让我们快速地浏览下之前文章中讲了些什么:

  • 一个生产者(Producer)是用于产生消息的用户应用程序;
  • 一个队列(Queue)是缓存区,用于储存消息;
  • 一个消费者(Consumer)是用于接收消息的用户应用程序。

RabbitMQ中消息传输模式的核心思想是生产者绝不会直接向队列发送任何消息。实际上,通常情况下生产者甚至都不会知道消息是否会被发送至队列。

  生产者会将消息发送至交换(exchange)交换并不复杂。一方面它从生产者中接受消息,另一方面将消息推送至队列。交换必须知道,当它接受一个消息时,它该怎么做。是否这个消息会附加至一个特殊的队列?是否它会附加至许多队列?或者它会被丢弃。这个规则用交换类型(exchange type)来定义。

有一些可用的交换类型直接分发(direct)通配分发(topic)headers复制分发(fanout)。我们将会集中讲最后一个——fanout。我们创建一个交换,类型为fanout,并取名为logs:

channel.exchange_declare(exchange='logs',
exchange_type='fanout')

fanout交换非常简单。顾名思义,它会将所有它知道的接收队列的消息都广播出去。而这也正是我们的日志系统所需要的。

  现在,我们可以发布已经命名好的队列了:

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

临时队列

  你也许还记得在之前的文章中,我们需要给队列取名。但是呢,给队列命名太麻烦了——我们需要将workers指定到同一个队列。当你需要在生产者和消费者之间共享队列的时候,给队列命名又是很重要的。

  这种情形并不适合我们的日志系统。我们想要监听所有的消息,而不是部分消息。同时,我们仅对当前的流动消息感兴趣,而不是之前的消息。为了解决这个问题,我们需要做两件事情。

  首先,无论何时我们连接到RabbitMQ,我们需要一个新的空队列。为此,我们创建一个随机命名的队列,或者更好的是,让RabbitMQ Server来给我们创建一个随机命名的队列。因此,我们可以利用queue_declare命令,设置queuq参数为空:

result = channel.queue_declare(queue='')

此时,result.method.queue会包含一个随机命名的队列,比如说,它会和amq.gen-JzTY20BRgKO-HjmUJj0wLg类似。

  其次,一旦消息者的连接关闭,我们需要删除队列。这可以用exclusive参数搞定:

result = channel.queue_declare(queue='', exclusive=True)

绑定(Bindings)



  我们已经创建了一个fanout 交换和队列。现在我们需要告诉交换,将消息发送至队列。交换与队列之间的关系叫做绑定(Bindings)

channel.queue_bind(exchange='logs',
queue=result.method.queue)

  从现在开始,logs交换将会在我们的队列后追加消息。

代码



  生产者代码(emit_log.py):

# -*- coding: utf-8 -*-
import pika
import sys connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel() channel.exchange_declare(exchange='logs', exchange_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()

  消费者代码(receive_log.py):

# -*- coding: utf-8 -*-
import pika connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel() channel.exchange_declare(exchange='logs', exchange_type='fanout') result = channel.queue_declare(queue='', 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(queue=queue_name, on_message_callback=callback, auto_ack=True) channel.start_consuming()

  开启四个终端,其中一个用于保存日志:

python3 receive_log.py > logs_from_rabbit.log

另一个用于观察日志输出:

python3 receive_log.py

日志产生:

python3 emit_log.py

监听绑定:

sudo rabbitmqctl list_bindings

运行截图如下:

  本次分享到此结束,感谢大家阅读~

RabbitMQ入门(三)订阅模式的更多相关文章

  1. RabbitMQ入门-消息订阅模式

    消息派发 上篇<RabbitMQ入门-消息派发那些事儿>发布之后,收了不少反馈,其中问的最多的还是有关消息确认以及超时等场景的处理. 楼主,有遇到消费者后台进程不在,但consumer连接 ...

  2. RabbitMQ入门-发布订阅模式

    兔子的Publish/Subscribe是这样的: 有个生产者P,X代表交换机,交换机绑定队列,消费者从队列中取得消息.每次有消息,先发到交换机中,然后由交换机负责发送到它已知的队列中. 生产者代码: ...

  3. Rabbitmq交换机三种模式介绍

    1.topic 将路由键和某模式进行匹配.此时队列需要绑定要一个模式上.符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词.因此“abc.#”能够匹配到“abc.def.ghi”,但是“abc. ...

  4. RabbitMQ的发布订阅模式(Publish/Subscribe)

    一.发布/订阅(Publish/Subscribe)模式 发布订阅是我们经常会用到的一种模式,生产者生产消息后,所有订阅者都可以收到.RabbitMQ的发布/订阅模型图如下: 1.该模式下生产者并不是 ...

  5. RabbitMQ简单应用の订阅模式

    订阅模式 公众号-->订阅之后才会收到相应的文章. 解读: 1.一个生产者,多个消费者 2.每个消费者都有自己的队列 3.生产者没有将消息直接发送到队列里,而是发送给了交换机(转发器)excha ...

  6. RabbitMQ入门-竞争消费者模式

    上一篇讲了个 哈喽World,现在来看看如果存在多个消费者的情况. 生产者: package com.example.demo; import com.rabbitmq.client.Channel; ...

  7. Java设计模式从精通到入门三 策略模式

    介绍 我尽量用最少的语言解释总结: Java23种设计模式之一,属于行为型模式.一个类的行为或者算法可以在运行时更改,策略对象改变context对象执行算法. 应用实例: ​ 以周瑜赔了夫人又折兵的例 ...

  8. RabbitMQ入门到进阶(Spring整合RabbitMQ&SpringBoot整合RabbitMQ)

    1.MQ简介 MQ 全称为 Message Queue,是在消息的传输过程中保存消息的容器.多用于分布式系统 之间进行通信. 2.为什么要用 MQ 1.流量消峰 没使用MQ 使用了MQ 2.应用解耦 ...

  9. RabbitMQ入门案例

    RabbitMQ入门案例 Rabbit 模式 https://www.rabbitmq.com/getstarted.html 实现步骤 构建一个 maven工程 导入 rabbitmq的依赖 启动 ...

随机推荐

  1. 原生js实现继承

    在多数语言中继承都很重要.JavaScript是一个基于原型的语言,这意味着对象可以直接从其他对象继承.以下列出几种常见的js继承方式. 原型链继承 function Father(){ this.s ...

  2. 爬虫工程师的unidbg入门教程

    现在很多的app使用了so加密,以后会越来越多.爬虫工程师可能会直接逆向app,看java代码,完成java层的算法破解,但是如果遇到so该怎么办呢?可能你会直接破解so,但是真的会有很多爬虫工程师会 ...

  3. Object 与 Function那神奇而混乱的搞基关系

    // Object 与 Function神奇而混乱的搞基关系... Object.__proto__ === Function.prototype; // true Object.__proto__ ...

  4. TCP&IP基础概念复习

    第一章概述 NII(National Information Infrastructure):国家信息基础设施 GII(Global Information Infrastructure):全球信息基 ...

  5. mybatis精讲(六)--二级缓存

    目录 简介 配置 源码 CachingExecutor 自定义二级缓存 # 加入战队 微信公众号 简介 上一章节我们简单了解了二级缓存的配置.今天我们详细分析下二级缓存以及为什么不建议使用二级缓存. ...

  6. pyinstaller打包py脚本Warning:lib not found等相关问题

    小爬从使用Pyinstaller打包py为exe文件以来,一直都会碰到Warning:lib not found等相关问题,诸如: 虽然大多数时候,您像我一样忽略这些warning,打包后的exe也能 ...

  7. Mongdb的基本操作及java中用法

    Mongdb中所有数据以Bson(类似JSON)的格式存在,可以存储集合,map,二进制文件等多种数据类型. 数据库的常用操作 use [数据库名称];//有就选中,没有就添加并选中show dbs; ...

  8. iptables匹配端口范围,映射,网络状态

    ####匹配端口范围:iptables -I INPUT -p tcp -m multiport --dport 21,22,23,24 -j ACCEPT <==次选iptables -I I ...

  9. 洛谷$P$2575 高手过招 博弈论

    正解:博弈论 解题报告: 传送门! 阿西$gql$又双叒被题意杀辣,,,再不好好学语文吃枣药丸$TT$ 然后在$get$规则之后还有什么问题嘛,,, 就和这题差不多了,一个$easy$的阶梯问题罢辽, ...

  10. 数据库中间件分片算法之enum

    前言 最近挺焦虑的,不知道未来该做什么,方向又是什么.只能用别慌,月亮也正在大海的某处迷茫.来安慰下自己.不过学习的初心咱们还是不要忘记.今天我们学习的是enum分片算法. 1.hash分区算法 2. ...