之前的教程里,我们只改进了我们的日志系统,我们使用直连交换机替代了扇形交换机,从只能盲目的广播消息改进为有可能选择性的接收日志
尽管直连交换机能够改善我们的系统,但是它也有它的限制--没办法基于多个标准执行路由操作
在我们的日志系统中,我们不只希望订阅基于严重程度的日志,同时还希望订阅基于发送来源的日志。Unix根据syslog就是基于严重程度-severity(info/warn/crit...)和设备-facility(auth/cron/kern...)来路由日志的
主题交换机
发送到主题交换机(topic exchange)的消息不可以携带随意什么样子的路由器(routing key), 它的路由键是由一个.分隔开的词语列表。 这些单词随意是什么样的都可以。但最好是携带和它们消息有关系的词汇。比如: stock.usd.nyse等, 词语的个数可以随意,但是不要超过255字节。
绑定键也必须拥有同样的格式。主题交换机背后的逻辑跟直连交换机很相似--一个携带着特定路由键的消息会被主题交换机投递给绑定键与之相匹配的队列。但是它的绑定键和路由键有两个特殊的应用方式:
* (星号) 用来表示一个单词
# (井号) 用来表示任意数量(零个或者多个)单词
在这个例子中,我们发送的所有消息都是用来描述小动物的。发送的消息携带的路由键是由三个单词所组成的。这三个单词被两个.分隔开。路由键的第一个单词描述的是动物手脚的利索程度,第二个单词是动物的颜色,第三个是动物的种类。所以它看起来是这样的: <celerity>.<color>.<species>
我们创建了三个绑定: Q1的绑定键为*.orange.*, Q2的绑定键为*.*rabbit和lazy.#
这三个绑定键可以被总结为:
Q1对所有的橘黄色动物都感兴趣
Q2则是对所有的兔子和所有懒惰的动物感兴趣
代码整合
生产者:
#!/usr/bin/env python import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() exchange_name = 'logs4' channel.exchange_declare(exchange=exchange_name, exchange_type='topic') message = (' '.join(sys.argv[1:]) or "info: Hello World!") channel.basic_publish(exchange=exchange_name, routing_key='lazy.green.rabbit', body=message) print(" [x] Sent %r" % (message,)) connection.close()
消费者1:
#!/usr/bin/env python import pika connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() exchange_name = 'logs4' channel.exchange_declare(exchange=exchange_name, exchange_type='topic') queue_name = 'test1' result = channel.queue_declare(queue=queue_name, exclusive=True) # queue_name = result.method.queue channel.queue_bind(exchange=exchange_name, queue=queue_name, routing_key='lazy.*.dog') # * 表示的是一个单词, # 任意数量(0或者多个单词) 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, exclusive=True, auto_ack=False) # 在用完此队列后立即删除 channel.start_consuming()
消费者2:
#!/usr/bin/env python import pika connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() exchange_name = 'logs4' channel.exchange_declare(exchange=exchange_name, exchange_type='topic') queue_name = 'test2' result = channel.queue_declare(queue=queue_name, exclusive=True) channel.queue_bind(exchange=exchange_name, queue=queue_name, routing_key='*.*.rabbit') # 只接收到尾号为rabbit路由键的消息 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, exclusive=True, auto_ack=False) # 在用完此队列后立即删除 channel.start_consuming()
1. 绑定键为*的队列会取到一个路由键为空的消息吗?
不能, * 表示至少有一个单词, 路由键为空的是无法接收到的
2. 绑定键为#.*的队列会获取到一个名为..的路由键的消息吗? 它会取到一个路由键为单个单词的消息吗?
会的,绑定键为#可以是任意0或者多个, * 至少为一个单词,所有#.*可以获取到名为..路由键的消息。
3. a.*.#和a.#的区别在哪里?
a.*.#中间的*至少要有一个单词,也就是说第一个至少需要两个单词匹配,a和*。而a.#只需要a这个单词匹配成功即可。