在上篇文章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:

  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='topic_logs',
  8. type='topic')
  9. routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
  10. message = ' '.join(sys.argv[2:]) or 'Hello World!'
  11. channel.basic_publish(exchange='topic_logs',
  12. routing_key=routing_key,
  13. body=message)
  14. print " [x] Sent %r:%r" % (routing_key, message)
  15. 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:

  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='topic_logs',
  8. type='topic')
  9. result = channel.queue_declare(exclusive=True)
  10. queue_name = result.method.queue
  11. binding_keys = sys.argv[1:]
  12. if not binding_keys:
  13. print >> sys.stderr, "Usage: %s [binding_key]..." % (sys.argv[0],)
  14. sys.exit(1)
  15. for binding_key in binding_keys:
  16. channel.queue_bind(exchange='topic_logs',
  17. queue=queue_name,
  18. routing_key=binding_key)
  19. print ' [*] Waiting for logs. To exit press CTRL+C'
  20. def callback(ch, method, properties, body):
  21. print " [x] %r:%r" % (method.routing_key, body,)
  22. channel.basic_consume(callback,
  23. queue=queue_name,
  24. no_ack=True)
  25. 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:

  1. python receive_logs_topic.py "#"
python receive_logs_topic.py "#"

接收所有kern facility的log:

  1. python receive_logs_topic.py "kern.*"
python receive_logs_topic.py "kern.*"

仅仅接收critical的log:

  1. python receive_logs_topic.py "*.critical"
python receive_logs_topic.py "*.critical"

可以创建多个绑定:

  1. python receive_logs_topic.py "kern.*" "*.critical"
python receive_logs_topic.py "kern.*" "*.critical"

Producer产生一个log:"kern.critical" type:

  1. 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 使用主题进行消息分发的更多相关文章

  1. RabbitMQ消息队列(六):使用主题进行消息分发[转]

    在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity(严重级别)的log.但是,这也是它之所以叫做简单日志 ...

  2. RabbitMQ基本用法、消息分发模式、消息持久化、广播模式

    RabbitMQ基本用法 进程queue用于同一父进程创建的子进程间的通信 而RabbitMQ可以在不同父进程间通信(例如在word和QQ间通信) 示例代码 生产端(发送) import pika c ...

  3. RabbitMQ中交换机的消息分发机制

    RabbitMQ是一个消息代理,它接受和转发消息,是一个由 Erlang 语言开发的遵循AMQP协议的开源实现.在RabbitMQ中生产者不会将消息直接发送到队列当中,而是将消息直接发送到交换机(ex ...

  4. (转)RabbitMQ消息队列(六):使用主题进行消息分发

    在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity的log.但是,这也是它之所以叫做简单日志系统的原因, ...

  5. RabbitMQ消息队列(六):使用主题进行消息分发

    在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity的log.但是,这也是它之所以叫做简单日志系统的原因, ...

  6. RabbitMQ 入门教程(PHP版) 第五部分:通过主题进行消息分发(Topics)

    对于 Message 的 routing_key 字符串格式是有限制的:以点号"."分割的字符表,如php.laravel,并且长度不能超过 255 个字节. 对于 routing ...

  7. RabbitMq初探——消息分发

    消息分发 前言 我们在用到消息队列的场景,一般是处理逻辑复杂,耗时,所以将同步改为异步处理,接入队列,下游处理耗时任务. 队列消息数量很大,且下游worker进程(消费者)处理耗时长,所以就有了任务的 ...

  8. RabbitMQ消息分发轮询和Message Acknowledgment

    一.消息分发 RabbitMQ中的消息都只能存储在Queue中,生产者(下图中的P)生产消息并最终投递到Queue中,消费者(下图中的C)可以从Queue中获取消息并消费. 多个消费者可以订阅同一个Q ...

  9. 轻松搞定RabbitMQ(二)——工作队列之消息分发机制

    转自 http://blog.csdn.net/xiaoxian8023/article/details/48681987 上一篇博文中简单介绍了一下RabbitMQ的基础知识,并写了一个经典语言入门 ...

随机推荐

  1. checkbox、radio控件和文字不对齐

    一般使用html控件的时候  单选按钮和复选框的控件和文字不对齐 给input控件加上   style="vertical-align: middle; margin-top: -2px; ...

  2. Python 解决命令行删除、退格乱码问题

    安装了python 在命令行界面无法进行删除.退格 1 安装readline模块 两种方式:yum install -y readline-devel  readline 或者 下载文件https:/ ...

  3. AXI

    1.Cachable和bufferable 一个Master发出一个读写的request,中间要经过很多Buffer,最后才能送到memory.这些Buffer的添加是为了outstanding,ti ...

  4. 记录Queue插入的时候报错

    Queue 队列  特性  先进先出     和栈 Stack  非常相似 不过 栈 遵循 后进先出 Queue 和Stack 都存在数据并发的 问题 public static Queue<P ...

  5. Python:a,*args,**kwargs的理解

    1.何时用这些参数? 在任何时候继承类和重写方法时,应当用到’*args’和’**kwargs’将接收到的位置参数和键值参数给父类方法 . 2.一句话清晰说明: a是常规的变量类型,比如int,str ...

  6. 【Rails App】 应用服务器从Passenger切换为Puma, Grape出现线程安全问题

    Grape中的代码如下: def market @market ||= Market.find(params[:id]) end @market基于类层次的实例变量,属于非线程安全,如果一直使用多线程 ...

  7. 监控服务器配置(二)-----Grafana安装配置

    1.下载grafana安装包(linux版)到 /opt/minitor/grafana . 下载地址:https://download.csdn.net/download/a15565772151/ ...

  8. MySQL加入log_bin报错

    MySQL中二进制日志功能默认是关闭的,查看各种开启方式后,确定在配置文件中加入如下配置来开启该功能: [root@bogon /]# more /etc/my.cnf [mysqld] datadi ...

  9. makefile入门-初步了解

    自己开始学习makefile是由于VScode配置工程文件,看别人的配置不是很懂,于是决定入门学习下makefile. 先来说说makefile是做什么用的:makefile可以实现工程的自动化编译, ...

  10. 七、PyQT5控件——QSlider,QSpinBox

    一.Qslider QSlider是一个滑动条,可以设置成水平或垂直放置.最常用的方法允许用户在某一范围内互动该滑块,并将滑块的位置转换成一个整数值(int类型),这种方式可以在某一个范围内平顺的变动 ...