python3 RabbitMQ ( Publish/Subscribe)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yangxiaodong88/article/details/83181742

What This Tutorial Focuses On

在前面的教程中,我们创建了一个工作队列。工作队列后面的假设是,每个任务只交付给一个工作者。在这一部分中,我们将做一些完全不同的事情——我们将向多个消费者传递消息。这种模式称为“发布/订阅”。

为了说明这个模式,我们将构建一个简单的日志系统。它将由两个程序组成——第一个程序将发出日志消息,第二个程序将接收并打印它们

在我们的日志系统中,接收程序的每个运行副本都会收到消息。这样我们就可以运行一个接收器,并将日志指向磁盘;同时,我们可以运行另一个接收器在屏幕上看到日志。

实际上,已发布的日志消息将被广播到所有接收方。

交换机

在本教程的前几部分中,我们向队列发送和接收消息。现在是时候介绍Rabbit中的完整消息传递模型了。

让我们快速回顾一下我们在前面教程中介绍的内容:

  • 生产者是发送消息的用户应用程序。
  • 队列是存储消息的缓冲区。
  • 使用者是接收消息的用户应用程序。

RabbitMQ消息传递模型的核心思想是,生产者从不直接向队列发送任何消息。实际上,生产者常常根本不知道消息是否会传递到任何队列。

相反,生产者只能向交换器发送消息。交换是一件非常简单的事情。一边接收来自生产者的消息,另一边将消息推送到队列中。交换器必须确切地知道如何处理接收到的消息。它应该附加到特定的队列吗?它应该附加到许多队列中吗?或者它应该被丢弃。这些规则由exchange类型定义。
在这里插入图片描述

有几种交换类型可用:direct, topic, headers and fanout.。我们将关注最后一个——the fanout。让我们创建一个这种类型的交换,并将其称为日志:

channel.exchange_declare(exchange='logs',
                         exchange_type='fanout')

fanout
交换非常简单。顾名思义,它只是将接收到的所有消息广播给它所知道的所有队列。这正是我们需要的记录器。

Listing exchanges
To list the exchanges on the server you can run the ever useful rabbitmqctl:

sudo rabbitmqctl list_exchanges

In this list there will be some amq.* exchanges and the default (unnamed) exchange. These are created by default, but it is unlikely you’ll need to use them at the moment.

The default exchange
In previous parts of the tutorial we knew nothing about exchanges, but still were able to send messages to queues. That was possible because we were using a default exchange, which we identify by the empty string ("").

Recall how we published a message before:

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

The exchange parameter is the name of the exchange. The empty string denotes the default or nameless exchange: messages are routed to the queue with the name specified by routing_key, if it exists.

exchange参数是交换器的名称。空字符串表示默认的或无名的交换:如果存在的话,消息将以routing_key指定的名称路由到队列中。

现在,我们可以发布到我们的命名交换:

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

暂时队列

您可能还记得以前我们使用的队列有一个指定的名称(还记得hello和task_queue吗?)能够命名一个队列对我们来说至关重要——我们需要将工人指向同一个队列。当您希望在生产者和消费者之间共享队列时,为队列提供一个名称非常重要。

但我们的记录员却不是这样。我们希望听到所有日志消息,而不仅仅是其中的一个子集。我们也只对当前流动的消息感兴趣,而不是旧的消息。要解决这个问题,我们需要两件事。

首先,当我们连接到Rabbit时,我们需要一个新的空队列。为此,我们可以创建一个随机名称的队列,或者更好的做法是让服务器为我们选择一个随机队列名称。我们可以通过不向queue_declare提供队列参数来做到这一点:

result = channel.queue_declare()

在这一点上,result.method。队列包含一个随机队列名称。例如,它可能看起来像amq.gen . jzty20brgko - hjmujj0wlg。

其次,一旦关闭了使用者连接,就应该删除队列。有一个专属标志:

result = channel.queue_declare(exclusive=True)

您可以在关于队列的指南中了解关于排它标志和其他队列属性的更多信息。

Bindings
在这里插入图片描述

我们已经创建了一个fanout交换和一个队列。现在我们需要告诉exchange将消息发送到我们的队列。exchange和队列之间的关系称为绑定

channel.queue_bind(exchange='logs',
                   queue=result.method.queue)

From now on the logs exchange will append messages to our queue.

Listing bindings
You can list existing bindings using, you guessed it,

rabbitmqctl list_bindings

在这里插入图片描述

发出日志消息的生产者程序与前面的教程没有太大区别。最重要的变化是,我们现在希望将消息发布到日志交换中,而不是无名的消息。在发送时,我们需要提供路由_key,但是对于扇出交换,它的值被忽略了。下面是emit_log的代码。py脚本:

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

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

channel.exchange_declare(exchange='logs',
                         exchange_type='fanout')

message = ' '.join(sys.argv[1:]) or "info: Hello World!"
channel.basic_publish(exchange='logs',
                      routing_key='',
                      body=message)
print(" [x] Sent %r" % message)
connection.close()

如您所见,在建立连接之后,我们声明了exchange。这一步是必要的,因为向不存在的交换发布是被禁止的。

如果没有队列被绑定到exchange,消息将会丢失,但这对我们来说是可以的;如果还没有消费者在听,我们可以安全地抛弃这个信息。

receive_logs.py:

#!/usr/bin/env python
import pika

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

channel.exchange_declare(exchange='logs',
                         exchange_type='fanout')

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

channel.queue_bind(exchange='logs',
                   queue=queue_name)

print(' [*] Waiting for logs. To exit press CTRL+C')

def callback(ch, method, properties, body):
    print(" [x] %r" % body)

channel.basic_consume(callback,
                      queue=queue_name,
                      no_ack=True)

channel.start_consuming()

猜你喜欢

转载自blog.csdn.net/yangxiaodong88/article/details/83181742
今日推荐