3.6.5 RabbitMQ教程四 – Routing

What This Tutorial Focuses On

In the previous tutorial we built a simple logging system. We were able to broadcast log messages to many receivers.

之前的教程我们构建了简单的日志系统。我们已经能够广播日志消息给多个接收端。

In this tutorial we're going to add a feature to it - we're going to make it possible to subscribe only to a subset of the messages. For example, we will be able to direct only critical error messages to the log file (to save disk space), while still being able to print all of the log messages on the console.

这篇教程我们会在上一节的基础上给它添加一些特性 - 我们将使只订阅消息的一个子集成为可能。举例,我们将能够只将严重级报错信息定向至日志文件(保存在磁盘空间),同时仍然能够在控制台打印出所有的日志消息。

Bindings

In previous examples we were already creating bindings. You may recall code like:

之前的示例中我们已能够创建bindings。你也许会回想到那些代码,就像:

channel.queue_bind(exchange=exchange_name,
                   queue=queue_name)

A binding is a relationship between an exchange and a queue. This can be simply read as: the queue is interested in messages from this exchange.

一个binding就是一个交换和一个队列之间的关系。可以被简化理解为:队列对来自该交换的消息感兴趣

Bindings can take an extra routing_key parameter. To avoid the confusion with a basic_publish parameter we're going to call it a binding key. This is how we could create a binding with a key:

Bindings可以使用一个额外的routing_key参数。来避免与一个我们叫它为binding_key的basic_publish参数的冲突。以下是我们如何用一个key来创建一个binding:

channel.queue_bind(exchange=exchange_name,
                   queue=queue_name,
                   routing_key='black')

The meaning of a binding key depends on the exchange type. The fanout exchanges, which we used previously, simply ignored its value.

binding key的含义取决于交换类型。fanout交换,我们之前用过的,可以直接忽略该值。

Direct exchange

Our logging system from the previous tutorial broadcasts all messages to all consumers. We want to extend that to allow filtering messages based on their severity. For example we may want the script which is writing log messages to the disk to only receive critical errors, and not waste disk space on warning or info log messages.

我们之前教程的日志系统广播所有的消息到所有的消费端。我们想要扩展一下,即允许根据消息的严重性来过滤消息。比如我们可能想要把日志消息写入磁盘的脚本只接收严重级别的保持错,不要在警告或通知级别的日志消息上浪费磁盘空间。

We were using a fanout exchange, which doesn't give us too much flexibility - it's only capable of mindless broadcasting.

前面我们使用了一个fanout交换,这并没有给我们带来太多的灵活性 - 它只能进行无脑的广播

We will use a direct exchange instead. The routing algorithm behind a direct exchange is simple - a message goes to the queues whose binding key exactly matches the routing key of the message.

接下来我们会用一个direct交换类型替代。direct交换背后的路由算法很简单 - 一条去往队列的消息的binding_key可以和该消息的routing_key精确的匹配上。

To illustrate that, consider the following setup:

为了演示这点,仔细思考下面的设置:

                                             direct-exchange

In this setup, we can see the direct exchange X with two queues bound to it. The first queue is bound with binding key orange, and the second has two bindings, one with binding key black and the other one with green.

在上面的设置中,我们可以看到direct交换X绑定了两个队列。第一个队列绑定了名为orange的binding key,第二个队列有两个bindings,一个叫black另一个叫green

In such a setup a message published to the exchange with a routing key orange will be routed to queue Q1. Messages with a routing key of black or green will go to Q2. All other messages will be discarded.

在这个设置中,一条消息被发布到routing key为orange的交换会被路由至队列Q1. 发送至routing key为black或green的消息会去往Q2.所有其他的消息会被丢弃。

Multiple bindings

                                              multiple bindings

It is perfectly legal to bind multiple queues with the same binding key. In our example we could add a binding between X and Q1 with binding key black. In that case, the direct exchange will behave like fanout and will broadcast the message to all the matching queues. A message with routing key black will be delivered to both Q1 and Q2.

用同样的binding key绑定多个队列是完全合法的。在我们的示例中我们可以在X和Q1之间添加一个binding key为black的binding。这时,direct交换会像fanout一样工作并且会广播消息到所有匹配的队列。routing key为black的消息会被传递至Q1和Q2

Emitting logs

We'll use this model for our logging system. Instead of fanout we'll send messages to a direct exchange. We will supply the log severity as a routing key. That way the receiving script will be able to select the severity it wants to receive. Let's focus on emitting logs first.

我们将在我们的日志系统上使用上面这个模型。作为fanout的代替者我们会发送消息到direct交换。我们会提供日志严重级别作为routing key。那样一来接收脚本将能够选择它想要接收的严重级。让我们先来看看发送日志。

Like always we need to create an exchange first:

像往常一样,我们需要创建一个交换

channel.exchange_declare(exchange='direct_logs',
                         exchange_type='direct')

And we're ready to send a message:

然后我们准备发送一条消息:

channel.basic_publish(exchange='direct_logs',
                      routing_key=severity,
                      body=message)

To simplify things we will assume that 'severity' can be one of 'info', 'warning', 'error'.

为了简化问题,我们假设“严重级”可以是‘info’、‘warning’、‘error’之一。

Subscribing

Receiving messages will work just like in the previous tutorial, with one exception - we're going to create a new binding for each severity we're interested in.

接收消息会像之前教程中的那样工作,有一个例外 - 我们将为每个我们感兴趣的严重级创建一个新的binding

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

for severity in severities:
    channel.queue_bind(exchange='direct_logs',
                       queue=queue_name,
                       routing_key=severity)

Putting it all together

                                           python-four

emit_log_direct.py

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) > 1 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()

receive_logs_direct.py

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(queue='', 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(
    queue=queue_name, on_message_callback=callback, auto_ack=True)

channel.start_consuming()

If you want to save only 'warning' and 'error' (and not 'info') log messages to a file, just open a console and type:

如果你想只保存‘warning’和‘error’(没有’info‘)日志消息到文件里,就启动一个控制台并输入:

python receive_logs_direct.py warning error > logs_from_rabbit.log

If you'd like to see all the log messages on your screen, open a new terminal and do:

如果你想在你的屏幕上看到所有的日志消息,打开一个终端并且:

python receive_logs_direct.py info warning error
# => [*] Waiting for logs. To exit press CTRL+C

And, for example, to emit an error log message just type:

还有,比如,要发送一个报错日志消息只需:

python emit_log_direct.py error "Run. Run. Or it will explode."
# => [x] Sent 'error':'Run. Run. Or it will explode.'
posted @ 2020-01-18 16:22  InfiniteCodes  阅读(154)  评论(0编辑  收藏  举报