RabbitMQ入门(三)订阅模式
在之前的文章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入门(三)订阅模式的更多相关文章
- RabbitMQ入门-消息订阅模式
消息派发 上篇<RabbitMQ入门-消息派发那些事儿>发布之后,收了不少反馈,其中问的最多的还是有关消息确认以及超时等场景的处理. 楼主,有遇到消费者后台进程不在,但consumer连接 ...
- RabbitMQ入门-发布订阅模式
兔子的Publish/Subscribe是这样的: 有个生产者P,X代表交换机,交换机绑定队列,消费者从队列中取得消息.每次有消息,先发到交换机中,然后由交换机负责发送到它已知的队列中. 生产者代码: ...
- Rabbitmq交换机三种模式介绍
1.topic 将路由键和某模式进行匹配.此时队列需要绑定要一个模式上.符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词.因此“abc.#”能够匹配到“abc.def.ghi”,但是“abc. ...
- RabbitMQ的发布订阅模式(Publish/Subscribe)
一.发布/订阅(Publish/Subscribe)模式 发布订阅是我们经常会用到的一种模式,生产者生产消息后,所有订阅者都可以收到.RabbitMQ的发布/订阅模型图如下: 1.该模式下生产者并不是 ...
- RabbitMQ简单应用の订阅模式
订阅模式 公众号-->订阅之后才会收到相应的文章. 解读: 1.一个生产者,多个消费者 2.每个消费者都有自己的队列 3.生产者没有将消息直接发送到队列里,而是发送给了交换机(转发器)excha ...
- RabbitMQ入门-竞争消费者模式
上一篇讲了个 哈喽World,现在来看看如果存在多个消费者的情况. 生产者: package com.example.demo; import com.rabbitmq.client.Channel; ...
- Java设计模式从精通到入门三 策略模式
介绍 我尽量用最少的语言解释总结: Java23种设计模式之一,属于行为型模式.一个类的行为或者算法可以在运行时更改,策略对象改变context对象执行算法. 应用实例: 以周瑜赔了夫人又折兵的例 ...
- RabbitMQ入门到进阶(Spring整合RabbitMQ&SpringBoot整合RabbitMQ)
1.MQ简介 MQ 全称为 Message Queue,是在消息的传输过程中保存消息的容器.多用于分布式系统 之间进行通信. 2.为什么要用 MQ 1.流量消峰 没使用MQ 使用了MQ 2.应用解耦 ...
- RabbitMQ入门案例
RabbitMQ入门案例 Rabbit 模式 https://www.rabbitmq.com/getstarted.html 实现步骤 构建一个 maven工程 导入 rabbitmq的依赖 启动 ...
随机推荐
- tensorflow中models的安装
tensorflow中models的安装参看网址: 1. Tensorflow Object Detection API Windows Install Guide http://www.insigh ...
- hdu 6579 Operation (在线线性基)
传送门 •题意 一个数组a有n个数 m个操作 操作① 询问$[l,r]$区间的异或值 操作② 在数组末尾追加一个数x,数组长度变为$n+1$ 其中$l,r$不直接给出,其中$l=l%n+1,r=r%n ...
- 2018-8-10-C#-6.0-字符串-String-Interpolation
title author date CreateTime categories C# 6.0 字符串 String Interpolation lindexi 2018-08-10 19:16:52 ...
- Team Foundation Server 2015使用教程【8】:读取器tfs组的checkin权限修改
- 虚拟机中linux系统无法打开原保存的显示器配置解决方法
刚刚学习Linux,于是在虚拟机上装了一个redhat,有一次关机的时候,很长一段时间都没有关闭,似乎是死机了,于是我就用任务管理器给强制关闭了.然后再次开启系统就出现了这个问题,如下图所示: 当时我 ...
- Linux基础:Linux下常用命令
常用命令 shutdown 用来系统关机命令.shutdown指令可以关闭所有程序,并依用户的需要,进行重新开机或关机的动作. shutdown (选项)(参数) -c: 当执行"s ...
- TypeScript躬行记(7)——命名空间
TypeScript中的命名空间可将那些具有内在联系的接口.类或对象等代码组织在一起,既能隔离作用域,也能避免命名冲突,并且使得代码结构清晰,更易追踪.在命名空间内部,所有实体部分默认都是私有的,需要 ...
- DOCKER学习_008:Docker容器的运行最佳实践
一 容器分类 容器按用途大致可分为两类: 服务类容器,如 web server. database等 工具类容器,如cur容器, Iredis-cli容器 通常而言,服务类容器需要长期运行,所以使用 ...
- Jenkins的简单安装
系统:CentOS release 6.5 (Final) 一. 安装JDK 1.1 查询系统是否已安装OpenJDK Centos系统默认会安装OpenJDK,首先检查系统是否安装有jdk并且是Op ...
- 基于FPGA的SPI FLASH控制器设计
1.SPI FLASH的基本特征 本文实现用FPGA来设计SPI FLASH,FLASH型号为W25Q128BV.支持3种通信方式,SPI.Dual SPI和Quad SPI.FLASH的存储单元无法 ...