RabbitMQ学习笔记06:Topics

参考资料:RabbitMQ tutorial - Topics — RabbitMQ 

 

前言

在上一篇博文中我们使用direct类型的exchange改善了我们的日志系统,但是它仍然有一定的限制,它没有办法基于多个条件路由消息。

我们可能不仅仅希望基于日志级别(严重性)来订阅日志,也希望可以基于比如说日志的来源。比如syslog这个unix工具就是这么工作的,它会基于severity (info, warn, crit...) 和facility (auth, cron, kern...) 来路由。

这样就比较灵活了,比如我们可能希望获取crit级别的cron类型日志同时希望获取所有级别的kern类型日志。

想要实现的话,我们需要一个更复杂的exchange,叫做topic

 

Topic exchange

消息发送给topic类型的exchange的时候,不能使用随意的routing_key,它必须得是一个使用小数点分隔的单词列表。单词可以是随意的不过一般都是会和消息有关系。以下是一些有效的routing_key示例:"stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit"routing_key的大小限制是255字节。

binding_key也必须是同样的格式。topic exchange背后的逻辑其实和direct exchange是类似的,也是将消息发送给routing_keybinding_key匹配的队列。不过在binding_key中有两种特殊的情况:

  • * (star) 替换成一个单词。
  • # (hash) 替换成零或者多个单词,即任意个单词。

在本次的案例中,所有发送的消息都是用来描述动物的。消息发送时的routing_key会包含3个字段的信息

  • 敏捷性 celerity
  • 颜色
  • 物种 species

<celerity>.<colour>.<species>

*.orange.*绑定到队列Q1上,*.*.rabbitlazy.#绑定到队列Q2上。

通过刚才对routing_key的字段的解释以及绑定的关系,我们可以知道:

  • 队列Q1对于橙色的动物比较感兴趣。
  • 队列Q2对于兔子或者懒洋洋的动物比较感兴趣。

接下来我们举例说明一些带有具体的routing_key会进入的队列:

  • quick.orange.rabbit两个队列都会进入。
  • lazy.orange.elephant两个队列都会进入。
  • quick.orange.fox只会进入队列Q1。
  • lazy.brown.fox只会进入队列Q2。
  • lazy.pink.rabbit虽然它匹配了2个binding_key,但是它只会进入队列Q2一次。
  • quick.brown.fox没有任何的binding_key和它匹配,因此这条消息会被丢弃。

虽然上面我们说routing_key只有3个字段,但是由于lazy.#的存在,因此任意字段数都是可以的

  • orange被丢弃。
  • quick.orange.new.rabbit被丢弃。
  • lazy.orange.new.rabbit只会进入队列Q2。

topic exchange很强大,在某些条件下它可以等同于其他类型的exchange

  • 当仅使用#来绑定队列时,它可以接收任意的routing_key的消息,即忽略了routing_key,此时就等同于fanout
  • 如果#*都没有使用而仅使用字符串常量的时候,此时就等同于direct

 

Putting it all together

假设我们的routing_key的形式是<facility>.<severity>

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', exchange_type='topic')

routing_key = sys.argv[1] if len(sys.argv) > 2 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()

receive_logs_topic.py

#!/usr/bin/env python
import pika, sys, os

def main():
    connection = pika.BlockingConnection(
    pika.ConnectionParameters(host='localhost'))
    channel = connection.channel()

    channel.exchange_declare(exchange='topic_logs', exchange_type='topic')

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

    binding_keys = sys.argv[1:]
    if not binding_keys:
        sys.stderr.write("Usage: %s [binding_key]...\n" % 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.decode()))


    channel.basic_consume(
        queue=queue_name, on_message_callback=callback, auto_ack=True)

    channel.start_consuming()


if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print('Interrupted')
        try:
            sys.exit(0)
        except SystemExit:
            os._exit(0)

测试的方式和以往类似,开启多个终端来启动消费者进程,使用不同的binding_key,监听消息。

python receive_logs_topic.py "#"
python receive_logs_topic.py "kern.*"
python receive_logs_topic.py "*.critical"
python receive_logs_topic.py "kern.*" "*.critical" # 多个binding_key绑定

然后我们发几条消息测试一下。

python emit_log_topic.py "kern.critical" "A critical kernel error"
python emit_log_topic.py "kern.info" "A critical kernel error"
python emit_log_topic.py "space.critical" "A critical kernel error"
python emit_log_topic.py "log.info" "A critical kernel error"

 

总结

这篇博文介绍了topic类型的exchange,在fanoutdirect的基础上,通过模式匹配符号来实现更加灵活的消息和队列的匹配方式。

posted @ 2023-01-12 14:25  阿龙弟弟  阅读(21)  评论(0编辑  收藏  举报
回到顶部