RabbitMQ 使用主题进行消息分发
在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统。Consumer可以监听不同severity的log。但是,这也是它之所以叫做简单日志系统的原因,因为是仅仅能够通过severity设定。不支持更多的标准。
比如syslog unix的日志工具,它可以通过severity (info/warn/crit...) 和模块(auth/cron/kern...)。这可能更是我们想要的:我们可以仅仅需要cron模块的log。
为了实现类似的功能,我们需要用到topic exchange。
1. Topic exchange
对于Message的routing_key是有限制的,不能使任意的。格式是以点号“."分割的字符表。比如:"stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit"。你可以放任意的key在routing_key中,当然最长不能超过255 bytes。
对于routing_key,有两个特殊字符(在正则表达式里叫元字符):
- * (星号) 代表任意 一个单词
- # (hash) 0个或者多个单词
请看下面一个例子:

Producer发送消息时需要设置routing_key,routing_key包含三个单词和两个点号。第一个key是描述了celerity(灵巧,敏捷),第二个是colour(色彩),第三个是species(物种):"<celerity>.<colour>.<species>"。
在这里我们创建了两个绑定: Q1 的binding key 是"*.orange.*"; Q2 是 "*.*.rabbit" 和 "lazy.#":
- Q1 感兴趣所有orange颜色的动物
- Q2 感兴趣所有的rabbits和所有的lazy的
比如routing_key是 "quick.orange.rabbit"将会发送到Q1和Q2中。消息"lazy.orange.elephant" 也会发送到Q1和Q2。但是"quick.orange.fox" 会发送到Q1;"lazy.brown.fox"会发送到Q2。"lazy.pink.rabbit" 也会发送到Q2,但是尽管两个routing_key都匹配,它也只是发送一次。"quick.brown.fox" 会被丢弃。
如果发送的单词不是3个呢? 答案要看情况,因为#是可以匹配0个或任意个单词。比如"orange" or "quick.orange.male.rabbit",它们会被丢弃。如果是lazy那么就会进入Q2。类似的还有 "lazy.orange.male.rabbit",尽管它包含四个单词。
Topic exchange和其他exchange
由于有"*" (star) and "#" (hash), Topic exchange 非常强大并且可以转化为其他的exchange:
如果binding_key 是 "#" - 它会接收所有的Message,不管routing_key是什么,就像是fanout exchange。
如果 "*" (star) and "#" (hash) 没有被使用,那么topic exchange就变成了direct exchange。
2. 代码实现
现在我们要refine我们上篇的日志系统。routing keys 有两个部分: "<facility>.<severity>"。
The code for emit_log_topic.py:
- #!/usr/bin/env python
- import pika
- import sys
- connection = pika.BlockingConnection(pika.ConnectionParameters(
- host='localhost'))
- channel = connection.channel()
- channel.exchange_declare(exchange='topic_logs',
- type='topic')
- routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
- message = ' '.join(sys.argv[2:]) or 'Hello World!'
- channel.basic_publish(exchange='topic_logs',
- routing_key=routing_key,
- body=message)
- print " [x] Sent %r:%r" % (routing_key, message)
- connection.close()
#!/usr/bin/env python
import pika
import sys connection = pika.BlockingConnection(pika.ConnectionParameters(
host='localhost'))
channel = connection.channel() channel.exchange_declare(exchange='topic_logs',
type='topic') routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
message = ' '.join(sys.argv[2:]) or 'Hello World!'
channel.basic_publish(exchange='topic_logs',
routing_key=routing_key,
body=message)
print " [x] Sent %r:%r" % (routing_key, message)
connection.close()
The code for receive_logs_topic.py:
- #!/usr/bin/env python
- import pika
- import sys
- connection = pika.BlockingConnection(pika.ConnectionParameters(
- host='localhost'))
- channel = connection.channel()
- channel.exchange_declare(exchange='topic_logs',
- type='topic')
- result = channel.queue_declare(exclusive=True)
- queue_name = result.method.queue
- binding_keys = sys.argv[1:]
- if not binding_keys:
- print >> sys.stderr, "Usage: %s [binding_key]..." % (sys.argv[0],)
- sys.exit(1)
- for binding_key in binding_keys:
- channel.queue_bind(exchange='topic_logs',
- queue=queue_name,
- routing_key=binding_key)
- print ' [*] Waiting for logs. To exit press CTRL+C'
- def callback(ch, method, properties, body):
- print " [x] %r:%r" % (method.routing_key, body,)
- channel.basic_consume(callback,
- queue=queue_name,
- no_ack=True)
- channel.start_consuming()
#!/usr/bin/env python
import pika
import sys connection = pika.BlockingConnection(pika.ConnectionParameters(
host='localhost'))
channel = connection.channel() channel.exchange_declare(exchange='topic_logs',
type='topic') result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue binding_keys = sys.argv[1:]
if not binding_keys:
print >> sys.stderr, "Usage: %s [binding_key]..." % (sys.argv[0],)
sys.exit(1) for binding_key in binding_keys:
channel.queue_bind(exchange='topic_logs',
queue=queue_name,
routing_key=binding_key) print ' [*] Waiting for logs. To exit press CTRL+C' def callback(ch, method, properties, body):
print " [x] %r:%r" % (method.routing_key, body,) channel.basic_consume(callback,
queue=queue_name,
no_ack=True) channel.start_consuming()
3. 运行和结果
接收所有的log:
- python receive_logs_topic.py "#"
python receive_logs_topic.py "#"
接收所有kern facility的log:
- python receive_logs_topic.py "kern.*"
python receive_logs_topic.py "kern.*"
仅仅接收critical的log:
- python receive_logs_topic.py "*.critical"
python receive_logs_topic.py "*.critical"
可以创建多个绑定:
- python receive_logs_topic.py "kern.*" "*.critical"
python receive_logs_topic.py "kern.*" "*.critical"
Producer产生一个log:"kern.critical" type:
- python emit_log_topic.py "kern.critical" "A critical kernel error"
python emit_log_topic.py "kern.critical" "A critical kernel error"
课后思考题:
- Will "*" binding catch a message sent with an empty routing key?
- Will "#.*" catch a message with a string ".." as a key? Will it catch a message with a single word key?
- How different is "a.*.#" from "a.#"?
转载自: anzhsoft: http://blog.csdn.net/anzhsoft/article/details/19633079
参考资料:
1. http://www.rabbitmq.com/tutorials/tutorial-five-python.html
RabbitMQ 使用主题进行消息分发的更多相关文章
- RabbitMQ消息队列(六):使用主题进行消息分发[转]
在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity(严重级别)的log.但是,这也是它之所以叫做简单日志 ...
- RabbitMQ基本用法、消息分发模式、消息持久化、广播模式
RabbitMQ基本用法 进程queue用于同一父进程创建的子进程间的通信 而RabbitMQ可以在不同父进程间通信(例如在word和QQ间通信) 示例代码 生产端(发送) import pika c ...
- RabbitMQ中交换机的消息分发机制
RabbitMQ是一个消息代理,它接受和转发消息,是一个由 Erlang 语言开发的遵循AMQP协议的开源实现.在RabbitMQ中生产者不会将消息直接发送到队列当中,而是将消息直接发送到交换机(ex ...
- (转)RabbitMQ消息队列(六):使用主题进行消息分发
在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity的log.但是,这也是它之所以叫做简单日志系统的原因, ...
- RabbitMQ消息队列(六):使用主题进行消息分发
在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity的log.但是,这也是它之所以叫做简单日志系统的原因, ...
- RabbitMQ 入门教程(PHP版) 第五部分:通过主题进行消息分发(Topics)
对于 Message 的 routing_key 字符串格式是有限制的:以点号"."分割的字符表,如php.laravel,并且长度不能超过 255 个字节. 对于 routing ...
- RabbitMq初探——消息分发
消息分发 前言 我们在用到消息队列的场景,一般是处理逻辑复杂,耗时,所以将同步改为异步处理,接入队列,下游处理耗时任务. 队列消息数量很大,且下游worker进程(消费者)处理耗时长,所以就有了任务的 ...
- RabbitMQ消息分发轮询和Message Acknowledgment
一.消息分发 RabbitMQ中的消息都只能存储在Queue中,生产者(下图中的P)生产消息并最终投递到Queue中,消费者(下图中的C)可以从Queue中获取消息并消费. 多个消费者可以订阅同一个Q ...
- 轻松搞定RabbitMQ(二)——工作队列之消息分发机制
转自 http://blog.csdn.net/xiaoxian8023/article/details/48681987 上一篇博文中简单介绍了一下RabbitMQ的基础知识,并写了一个经典语言入门 ...
随机推荐
- 16. 3Sum Closest (JAVA)
Given an array nums of n integers and an integer target, find three integers in nums such that the s ...
- typename的用法
template <class T> void foo() { typename T::iterator * iter; } class ContainsAType{ public: cl ...
- c# post方法亲测可用
/// <summary> /// Post接口方法 /// </summary> /// <param name="requestUri">& ...
- 十、JAVA面试简答
2.ASCII编码表的常识 3.&和&&,|和||的区别 下面我们就分成三组对问题进行分析:分别是&和&&,|和||及~和!. 1.&是按位与操 ...
- yum 安装时遇到“UnicodeDecodeError: 'ascii' codec”的问题
今天新安装了一个6.9系统,配置好本地yum源后,用yum安装时报了以上的错误信息,在/etc/yum.repos.d/目录下多出了TTT的一个目录 (手动问号),在百度上查了一些文档. 解决方法:1 ...
- Spring bean的生命流程
Spring 是一个轻量级的 J2EE 开源框架,其目标是降低企业级应用开发难度,提高企业级应用开发效率.在日程开发中,我们会经常使用 Spring 框架去构建应用.所以作为一个经常使用的框架,了解其 ...
- docker-3 Apache
docker 安装 Apache 环境 docker pull httpd 文件创建连接(这样就可以不用发布了,两个文件夹会自动同步文件) ln -s /root/jenkins_home/works ...
- LR 11 代理录制步骤
1.新一个HTTP录制,到下图步骤 按下图说明设置信息,点击update 选择rl里面的代理exe(wplus_init_wsock.exe),具体路径为下图 打开浏览器设置代理(Internet选项 ...
- yii2.0 邮件发送如何配置
邮件发送配置: 打开配置文件将下面代码添加到 components => [...]中(例:高级版默认配置在/common/config/main-local.php) 'mai ...
- makefile入门-初步了解
自己开始学习makefile是由于VScode配置工程文件,看别人的配置不是很懂,于是决定入门学习下makefile. 先来说说makefile是做什么用的:makefile可以实现工程的自动化编译, ...