预备知识
What This Tutorial Focuses On
在前面的教程中,我们构建了一个简单的日志系统。我们能够向许多接收器广播日志消息。
在本教程中,我们将为它添加一个特性——我们将使订阅消息的一个子集成为可能。例如,我们将能够仅将关键错误消息直接指向日志文件(以节省磁盘空间),同时仍然能够在控制台打印所有日志消息。
绑定
在前面的例子中,我们已经创建了绑定。您可能会回忆代码如下:
channel.queue_bind(exchange=exchange_name,
queue=queue_name)
绑定是交换器和队列之间的关系。这可以简单地理解为:队列对来自此交换器的消息感兴趣。
绑定可以使用额外的routing_key参数。为了避免与basic_publish参数混淆,我们将它称为绑定键。这就是我们如何创建一个键绑定:
channel.queue_bind(exchange=exchange_name,
queue=queue_name,
routing_key='black')
绑定键的含义取决于交换类型。我们以前使用过的fanout交换交换机,只是忽略了它的价值。
Direct exchange
我们的日志系统从以前的教程广播所有消息到所有消费者。我们希望扩展此功能,以允许根据消息的严重程度对其进行过滤。例如,我们可能希望将日志消息写入磁盘的脚本只接收关键错误,而不是在警告或信息日志消息上浪费磁盘空间。
我们使用的是fanout交换器,这并没有给我们太多的灵活性——它只能够进行盲目的广播。
我们将使用直接交换。直接交换背后的路由算法很简单——消息转到队列,其绑定键与消息的路由键完全匹配。
为了说明这一点,考虑以下设置:
在这个设置中,我们可以看到直接exchange X,它绑定了两个队列。第一个队列用绑定键橙色绑定,第二个队列有两个绑定,一个用绑定键黑色绑定,另一个用绿色绑定。
在这样的设置中,发布到交换器的带有路由关键字橙色的消息将被路由到队列Q1。带有黑色或绿色路由键的消息将转到Q2。所有其他消息将被丢弃。
多个绑定
使用相同的绑定键绑定多个队列是完全合法的。在我们的示例中,我们可以使用绑定键black在X和Q1之间添加绑定。在这种情况下,直接交换将表现得像扇出,并将消息广播到所有匹配的队列。带有路由密钥黑色的消息将同时发送到Q1和Q2。
发送日志
我们将在日志系统中使用这个模型。我们将发送消息到一个直接交换器,而不是fanout。我们将提供日志严重性作为路由键。这样,接收脚本就能够选择它希望接收的严重性。让我们首先关注发出日志。
像往常一样,我们需要首先创建一个交换:
channel.exchange_declare(exchange='direct_logs',
exchange_type='direct')
我们准备发送一个消息:
channel.basic_publish(exchange='direct_logs',
routing_key=severity,
body=message)
为了简化问题,我们假设“严重性”可以是“信息”、“警告”、“错误”。
订阅
接收消息的工作方式将与上一教程一样,只有一个例外——我们将为感兴趣的每个严重性创建一个新的绑定。
result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue
for severity in severities:
channel.queue_bind(exchange='direct_logs',
queue=queue_name,
routing_key=severity)
将代码放在一起来看下
The code for emit_log_direct.py:
#!/usr/bin/env python
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='direct_logs',
exchange_type='direct')
severity = sys.argv[1] if len(sys.argv) > 2 else 'info'
message = ' '.join(sys.argv[2:]) or 'Hello World!'
channel.basic_publish(exchange='direct_logs',
routing_key=severity,
body=message)
print(" [x] Sent %r:%r" % (severity, message))
connection.close()
The code for receive_logs_direct.py:
#!/usr/bin/env python
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='direct_logs',
exchange_type='direct')
result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue
severities = sys.argv[1:]
if not severities:
sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
sys.exit(1)
for severity in severities:
channel.queue_bind(exchange='direct_logs',
queue=queue_name,
routing_key=severity)
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()