RabbitMQ中交换机的消息分发机制
RabbitMQ是一个消息代理,它接受和转发消息,是一个由 Erlang 语言开发的遵循AMQP协议的开源实现。在RabbitMQ中生产者不会将消息直接发送到队列当中,而是将消息直接发送到交换机(exchange),交换机用来接受生产者发送的消息并将这些消息发送给绑定的队列,即:生产者-->交换机-->队列。
在RabbitMQ中最主要的三种交换机:1. fanout(广播交换机) 2. direct(直连交换机) 3. topic(话题交换机)
1. fanout(广播交换机)
fanout会将接受到的所有消息广播到它所绑定的所有队列当中(每个消费者都会收到所有的消息),对于广播交换机,消息路由键routing_key和队列绑定键routing_key的作用都会被忽略。
fanout生产者:
import pika class RabbitProducer(object):
"""
与RabbitMq服务器建立连接
""" def __init__(self):
self.conn = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost', port=5672)
)
self.channel = self.conn.channel() # 声明一个exchange交换机,交换机的类型为fanout广播.
self.channel.exchange_declare(
exchange='fanout_exchange', exchange_type='fanout', durable=True
) def send_msg(self, message):
"""
routing_key:绑定的key
:param message:
:return:
"""
self.channel.basic_publish(
exchange='fanout_exchange',
routing_key='', # 因为exchange的类型为fanout,所以routing_key的数值在这里将被忽略
body=message,
properties=pika.BasicProperties(
delivery_mode=2,
# 消息进行持久化(防止服务器挂掉.)===> 如果没有queue绑定到这个exchange交换机,这个参数是没有的.
)) def close(self):
self.conn.close() if __name__ == "__main__":
rabbit_producer = RabbitProducer()
for i in range(10):
message = 'hello world {}!'.format(i)
rabbit_producer.send_msg(message)
消费者consumer1:
import pika
import uuid class RabbitConsumer(object):
"""
fanout 消费者1
""" def __init__(self):
self.conn = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost', port=5672)
)
self.channel = self.conn.channel() # 声明一个队列queue_consumer1,并进行持久化(防止服务器挂掉),exclusive设置为false
self.channel.queue_declare(
exclusive=False, durable=True, queue='queue_consumer1'
) # 声明一个exhange交换机,其类型为fanout广播类型 与生产者的交换机一致
self.channel.exchange_declare(
exchange='fanout_exchange', exchange_type='fanout', durable=True
) # 将队列queue_consumer1与该exchange交换机进行绑定
self.channel.queue_bind(exchange='fanout_exchange', queue='queue_consumer1') def call_back(self, method, body):
"""
消费者对消息进行确认,防止消费者挂掉.
:param method:
:param body:
:return:
"""
self.channel.basic_ack(delivery_tag=method.delivery_tag)
print('接收到的消息为:{}'.format(str(body))) def receive_msg(self):
print('consumer1开始接受消息...')
# 当上一条消息未确认时,会告知RabbitMQ不要再发送消息给这个消费者了 可以控制流量
self.channel.basic_qos(prefetch_count=1)
self.channel.basic_consume(
consumer_callback=self.call_back,
queue='queue_consumer1',
no_ack=False, # 消费者对消息进行确认,防止消费者挂掉
consumer_tag=str(uuid.uuid4())
) def consume(self):
self.receive_msg()
self.channel.start_consuming() if __name__ == '__main__':
rabbit_consumer = RabbitConsumer()
rabbit_consumer.consume()
消费者consumer2:
import pika
import uuid class RabbitConsumer(object):
def __init__(self):
self.conn = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost', port=5672)
)
self.channel = self.conn.channel() # 声明一个队列queue_consumer2,并进行持久化(防止服务器挂掉),exclusive设置为false
self.channel.queue_declare(
exclusive=False, durable=True, queue='queue_consumer2'
) # T声明一个exhange交换机,其类型为fanout广播类型
self.channel.exchange_declare(
exchange='fanout_exchange', exchange_type='fanout', durable=True
) # 将队列queue_consumer2与该exchange交换机进行绑定
self.channel.queue_bind(exchange='fanout_exchange', queue='queue_consumer2') def call_back(self, method, body):
"""
消费者对消息进行确认,防止消费者挂掉.
:param method:
:param body:
:return:
"""
self.channel.basic_ack(delivery_tag=method.delivery_tag)
print('接收到的消息为:{}'.format(str(body))) def receive_msg(self):
print('consumer2开始接受消息...')
self.channel.basic_consume(
consumer_callback=self.call_back,
queue='queue_consumer2',
no_ack=False,
consumer_tag=str(uuid.uuid4())
) def consume(self):
self.receive_msg()
self.channel.start_consuming() if __name__ == '__main__':
rabbit_consumer = RabbitConsumer()
rabbit_consumer.consume()
fanout会将接受到的所有消息广播到消费者consumer1和消费者consumer2,交换机的缺陷:它只能无意识的播放,不够灵活地控制消息广播给指定的消费者
2. direct(直连交换机)
对于direct,根据绑定键判定应该将数据发送至哪个队列,消息进入队列,其绑定秘钥(routing_key)与消息的路由秘钥要完全匹配,当exchange使用相同的绑定秘钥(routing_key)去绑定多个队列也是合法的,在这种情况下direct exchange的效果等同于fanout exchange,交换机会将消息广播到所有匹配的队列当中。 direct生产者:
import pika class RabbitProducer(object):
"""
与RabbitMq服务器建立连接
""" def __init__(self):
self.conn = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost', port=5672)
)
self.channel = self.conn.channel() # 声明一个exchange交换机,交换机的类型为direct
self.channel.exchange_declare(
exchange='direct_exchange', exchange_type='direct', durable=True
) def send_msg(self, routing_key, message):
"""
:param routing_key: 消息的路由键 本例中为routing_info
:param message: 生成者发送的消息
:return:
"""
self.channel.basic_publish(
exchange='direct_exchange',
routing_key=routing_key,
body=message,
properties=pika.BasicProperties(
delivery_mode=2,
# 消息进行持久化(防止服务器挂掉.)===> 如果没有queue绑定到这个exchange交换机,这个参数是没有的.
)) def close(self):
self.conn.close() if __name__ == "__main__":
rabbit_producer = RabbitProducer()
routing_key = 'routing_info'
for i in range(10):
message = 'hello world {}!'.format(i)
print('生产者发送的消息为:{}'.format(message))
rabbit_producer.send_msg(routing_key, message)
direct消费者:
import pika
import uuid class RabbitConsumer(object):
"""
消费者(订阅者)
""" def __init__(self):
self.conn = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost', port=5672)
)
self.channel = self.conn.channel() # 消息持久化
self.channel.queue_declare(
exclusive=False, durable=True, queue='task_queue'
) # 交换机类型为direct.
self.channel.exchange_declare(
exchange='direct_exchange', exchange_type='direct', durable=True
) # 将队列与该exchange交换机进行绑定
routing_keys = ['routing_info', 'aaa']
for routing_key in routing_keys:
self.channel.queue_bind(
exchange='direct_exchange', queue='task_queue', routing_key=routing_key
) # 如果生产者发生消息的routing_key与消费者绑定队列的routing_key相同则成功发送 def call_back(self, channel, method, properties, body):
"""
消费者对消息进行确认,防止消费者挂掉
:param channel:
:param method:
:param properties:
:param body:
:return:
"""
self.channel.basic_ack(delivery_tag=method.delivery_tag)
print('接收到的消息为:{}'.format(str(body))) def receive_msg(self):
print('开始接受消息...')
self.channel.basic_qos(prefetch_count=1) # TODO 告诉RabbitMQ,不要向我发送新的消息.
self.channel.basic_consume(
consumer_callback=self.call_back,
queue='task_queue',
no_ack=False,
consumer_tag=str(uuid.uuid4())
) def consume(self):
self.receive_msg()
self.channel.start_consuming() if __name__ == '__main__':
rabbit_consumer = RabbitConsumer()
rabbit_consumer.consume()
direct直连交换机相当于是fanout的升级版,当消费者的队列绑定的秘钥routing_key与生产者的routing_key相同时,消费者就会收到消息;当所有消费者的队列所绑定的routing_key都一样且与生产者相同时,就相当于fanout交换机
3. topic(话题交换机)
direct(直连交换机)虽然相当于fanout的升级版,但它仍然有局限性,它不能根据多个标准进行路由;topic(话题交换机)可以很好地解决这一问题:
(1) 如果消息的路由秘钥与队列的绑定秘钥符合匹配规则,topic就会将消息发送到相应的队列当中
(2) 对于绑定键(routing_key)有两个特殊的情况: * (星号)可以代替一个单词,#(散列)可以替代零个或多个单词
(3) 对于发送到topic交换机消息的routing_key如果包含特殊字符,只能是由"."分割的单词表,如("zhangsan.lisi")
topic 生产者:
import pika class RabbitProducer(object):
def __init__(self):
self.conn = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost', port=5672)
)
self.channel = self.conn.channel() # 声明交换机,交换机的类型为topic
self.channel.exchange_declare(
exchange='logs_topic', exchange_type='topic', durable=True
) def send_msg(self, routing_key, message):
"""
:param routing_key: 消息的路由键
:param message: 生成者发送的消息
:return:
"""
self.channel.basic_publish(
exchange='logs_topic',
routing_key=routing_key,
body=message,
properties=pika.BasicProperties(
delivery_mode=2,
# 消息进行持久化
)) def close(self):
self.conn.close() if __name__ == "__main__":
rabbit_producer = RabbitProducer()
routing_keys = ['info', "debug", "a.debug.b", "a.info.b"]
for routing_key in routing_keys:
message = 'hello world! {}'.format(routing_key)
print('生产者发送的消息为:{}'.format(message))
rabbit_producer.send_msg(routing_key, message)
topic 消费者1 --> 实现fanout交换机:
"""
当topic交换机使用#绑定键绑定队列时,此时topic交换机就会将消息广播到所有的队列当中,
不管消息的路由秘钥如何,此时topic交换机的效果等同于fanout:发送所有消息都会接受到
"""
import pika
import uuid class RabbitConsumer(object):
def __init__(self):
self.conn = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost', port=5672)
)
self.channel = self.conn.channel() # 消息持久化
self.channel.queue_declare(
exclusive=False, durable=True, queue='task_queue'
) # 声明交换机,其类型为topic
self.channel.exchange_declare(
exchange='logs_topic', exchange_type='topic', durable=True
) # 将队列与该交换机进行绑定
routing_keys = ['#'] # 使用#绑定键时,它将接受所有的消息,同fanout效果一样.
for routing_key in routing_keys:
self.channel.queue_bind(
exchange='logs_topic', queue='task_queue', routing_key=routing_key
) def call_back(self, channel, method, properties, body):
"""
消费者对消息进行确认,防止消费者挂掉
:param channel:
:param method:
:param properties:
:param body:
:return:
"""
self.channel.basic_ack(delivery_tag=method.delivery_tag)
print('接收到的消息为:{}'.format(str(body))) def receive_msg(self):
print('开始接受消息...')
self.channel.basic_qos(prefetch_count=1)
self.channel.basic_consume(
consumer_callback=self.call_back,
queue='task_queue',
no_ack=False, # 消费者对消息进行确认
consumer_tag=str(uuid.uuid4())
) def consume(self):
self.receive_msg()
self.channel.start_consuming() if __name__ == '__main__':
rabbit_consumer = RabbitConsumer()
rabbit_consumer.consume()
topic 消费者2 --> 实现direct交换机:
"""
当topic交换机没有使用*和#匹配符绑定键绑定队列时,此时topic交换机的效果等同于direct,
会收到key相匹配的消息 如:info debug
"""
import pika
import uuid class RabbitConsumer(object):
def __init__(self):
self.conn = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost', port=5672)
)
self.channel = self.conn.channel() # 消息持久化
self.channel.queue_declare(
exclusive=False, durable=True, queue='work_queue'
) #
# 声明交换机,其类型为topic
self.channel.exchange_declare(
exchange='logs_topic', exchange_type='topic', durable=True
) # 将队列与交换机进行绑定
routing_keys = ['info', 'debug']
for routing_key in routing_keys:
self.channel.queue_bind(
exchange='logs_topic', queue='work_queue', routing_key=routing_key
) def call_back(self, channel, method, properties, body):
"""
消费者对消息进行确认,防止消费者挂掉
:param channel:
:param method:
:param properties:
:param body:
:return:
"""
self.channel.basic_ack(delivery_tag=method.delivery_tag)
print('接收到的消息为:{}'.format(str(body))) def receive_msg(self):
print('开始接受消息...')
self.channel.basic_qos(prefetch_count=1)
self.channel.basic_consume(
consumer_callback=self.call_back,
queue='work_queue',
no_ack=False, # 消费者对消息进行确认
consumer_tag=str(uuid.uuid4())
) def consume(self):
self.receive_msg()
self.channel.start_consuming() if __name__ == '__main__':
rabbit_consumer = RabbitConsumer()
rabbit_consumer.consume()
topic 消费者3 --> 实现*.x.* 消息匹配:
"""
匹配任意点分割的单词 生产者发送的:a.debug.b 则匹配了'*.debug.*'
生产者发送的:a.info.b 则匹配了'*.info.*'
"""
import pika
import uuid class RabbitConsumer(object):
def __init__(self):
self.conn = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost', port=5672)
)
self.channel = self.conn.channel() # 消息持久化
self.channel.queue_declare(
exclusive=False, durable=True, queue='other_queue'
) # 声明交换机,其类型为topic
self.channel.exchange_declare(
exchange='logs_topic', exchange_type='topic', durable=True
) # 将队列与交换机进行绑定
routing_keys = ['*.info.*', '*.debug.*', 'dfdf*']
for routing_key in routing_keys:
self.channel.queue_bind(
exchange='logs_topic', queue='other_queue', routing_key=routing_key
) def call_back(self, channel, method, properties, body):
"""
消费者对消息进行确认,防止消费者挂掉
:param channel:
:param method:
:param properties:
:param body:
:return:
"""
self.channel.basic_ack(delivery_tag=method.delivery_tag)
print('接收到的消息为:{}'.format(str(body))) def receive_msg(self):
print('开始接受消息...')
self.channel.basic_qos(prefetch_count=1)
self.channel.basic_consume(
consumer_callback=self.call_back,
queue='other_queue',
no_ack=False, # 消费者对消息进行确认
consumer_tag=str(uuid.uuid4())
) def consume(self):
self.receive_msg()
self.channel.start_consuming() if __name__ == '__main__':
rabbit_consumer = RabbitConsumer()
rabbit_consumer.consume()
topic消费者执行结果:
消费者1:

消费者2:

消费者3:

RabbitMQ中交换机的消息分发机制的更多相关文章
- delphi VCL研究之消息分发机制-delphi高手突破读书笔记
1.VCL 概貌 先看一下VCL类图的主要分支,如图4.1所示.在图中可以看到,TObject是VCL的祖先类,这也是Object Pascal语言所规定的.但实际上,TObject以及TObject ...
- Cocos2d-x 3.0 屏幕触摸及消息分发机制
***************************************转载请注明出处:http://blog.csdn.net/lttree************************** ...
- 如何在项目中引入MetaQ消息收发机制
当需要异步发送和接收大量消息时,需要在Crystal项目中引入MetaQ消息收发机制. 关于MetaQ使用的官方例子可参考:https://github.com/killme2008/Metamorp ...
- 轻松搞定RabbitMQ(二)——工作队列之消息分发机制
转自 http://blog.csdn.net/xiaoxian8023/article/details/48681987 上一篇博文中简单介绍了一下RabbitMQ的基础知识,并写了一个经典语言入门 ...
- RabbitMQ基本用法、消息分发模式、消息持久化、广播模式
RabbitMQ基本用法 进程queue用于同一父进程创建的子进程间的通信 而RabbitMQ可以在不同父进程间通信(例如在word和QQ间通信) 示例代码 生产端(发送) import pika c ...
- Android 消息分发机制
Android 中针对耗时的操作,放在主线程操作,轻者会造成 UI 卡顿,重则会直接无响应,造成 Force Close.同时在 Android 3.0 以后,禁止在主线程进行网络请求. 针对耗时或者 ...
- Android正在使用Handler实现消息分发机制(零)
演讲前,AsyncTask文章.我们在最后谈到.AsyncTask它是利用Handler异步消息处理机制,操作结果.使用Message回到主线程,从而执行UI更新线程. 而在我们的日常开发工作,Han ...
- Android中View的事件分发机制
简介 事件也称MotionEvent,事件分发机制就是对MotionEvent事件的分发过程,即当一个MotionEvent发生之后,系统需要把这个事件传递给一个具体的View. 点击事件的分发过程由 ...
- Android正在使用Handler实现消息分发机制(两)
在开始这篇文章之前,.首先,我们在总结前两篇文章Handler, Looper和MessageQueue像一些关键点: 0)在创建线程Handler之前,你必须调用Looper.prepare(), ...
随机推荐
- Python 条件、循环、异常处理
一.条件语句 1.布尔值 条件语句中,判断条件的值一般是布尔值.即条件为真时,将执行什么,条件为假时,将执行什么. 下面的值在作为布尔表达式的时候,会被解释器看做假(false): False ...
- 网络&协议目录
HTTP [基础] HTTP入门学习 网络基础 数据传输 网站架构演化 连接管理 缓存 [组成] URL 报文起始行 报文首部 [结构] Web服务器 代理 网关.隧道和中继 [安全] Web攻击技术 ...
- linux mint18 cinnamon 64bit 安装 docker
参考官方文档:https://docs.docker.com/engine/installation/linux/ubuntu/ 1. 安装一些使 apt 可以使用 https 的源 sudo apt ...
- C# - 引用类型
引用类型(Reference Type) C#是一门使用OOP技术的编程语言(Object Oriented Programming 面向对象编程)面向对象最重要的特性就是接口.继承.多态 C#中所有 ...
- Python学习笔记-Django连接SQLSERVER
Django连接SQLSERVER使用的是odbc驱动. CentOS下安装django-obdc-azure时需安装依懒 yum install gcc yum install gcc-c++ yu ...
- c++入门篇八
构造函数的调用规则: 系统会提供三个函数,一个是默认的构造函数(无参,函数体为空),一个是拷贝构造函数(无参,函数体为空),一个是析构函数,对类中非静态成员属性简单值拷贝\如果用户定义了拷贝构造函数, ...
- Luogu 3371【模板】单源最短路径
Luogu 3371[模板]单源最短路径 第一次写博客用图论题来试一试 接下来是正文部分 题目描述 如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 输入输出格式 输入格式: 第一行包 ...
- CentOs7.5安装PostgreSQL11
前言 本章介绍在CentOs上安装一个PostgreSQL数据库 下一篇可能是安装 Redis 本篇使用的服务器是已经安装过Python/Nginx等常用软件的环境,因此在安装过程中可能会遇到按照本章 ...
- unity iOS本地代码总结(一)
1. 项目能直接运行了,但是代码的实际数据流动任然会有问题. 2. unity的代码能这么简单的被调用简直是奇迹一样,不需要大的改动就能够使用. 3. 目前需要注意的问题就是,unity的内容还太少, ...
- 移动端web app开发学习笔记
移动web和pc端web以及web app 移动web开发跟web前端开发差别很小,使用的技术都是html+css+js.手机网页可以理解成pc网页的缩小版加一些触摸特性.在浏览器中进行的网页开发,最 ...