一.介绍
RabbitMQ是一个消息代理:它接受和转发消息.类似于邮局:将消息发给邮局,邮局再把消息分发给目标.在这个比喻中 RabbitMQ是邮箱, 邮局和邮递员小哥.
RabbitMQ和邮局这两者之间的主要区别是它不会处理纸质邮件,取而代之的是接收、存储和发送二进制数据块,也就是我们通常所说的消息。
RabbitMQ和消息中,通常会使用一些专业术语。
生产:生产意味着就是发送。 发送消息的程序是一个生产者。下图代表一个消息的生产者:
队列:这里的队列是指一个名称,但是名称所代表的队列实体寄存在RabbitMQ服务器端中。 虽然消息流过RabbitMQ和您的应用程序,但它们只能存储在队列中。 队列只受主机的内存和磁盘的限制,它本质上是一个大的消息缓冲区。 许多生产者可以发送消息到一个队列,许多消费者可以尝试从一个队列接收数据。 下图代表一个队列:
消费:消费具有与接收相似的含义。 消费者是一个主要等待接收消息的程序。下图代表消息的消费者:
请注意,生产者,消费者和代理不必驻留在同一主机上; 实际上在大多数应用中他们都没有在同一台主机上。
(使用python脚本链接)
我们将用Python编写两个程序; 发送单个消息的生产者,以及接收消息并将其打印出来的消费者。我们将掩盖Python API中的一些细节,专注于这个非常简单的事情,只是为了开始。它是消息传递的“Hello World”。
在下图中,“P”是我们的生产者,“C”是我们的消费者。中间的框是一个队列 - RabbitMQ代表消费者保留的消息缓冲区。
Python客户端库
RabbitMQ说多种协议。本教程使用AMQP 0-9-1,它是一种开放的,通用的消息传递协议。RabbitMQ有许多不同语言的客户端。我们将使用RabbitMQ提供的Python客户端。
现在我们有了Python 客户端的及其依赖项,我们可以编写一些代码.
发出消息
我们的第一个程序send.py将向队列发送一条消息。我们需要做的第一件事是建立与RabbitMQ服务器的连接。
#!/usr/bin/env python
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
我们现在已连接到本地计算机上的代理 - 所以是localhost。如果我们想要连接到不同机器上的代理,我们只需在此处指定其名称或IP地址。
接下来,在发送之前,我们需要确保收件人队列存在。如果我们向不存在的位置发送消息,RabbitMQ将丢弃该消息。让我们创建一个将要传递消息的hello队列:
channel.queue_declare(queue = 'hello')
此时我们已准备好发送消息。我们的第一条消息只包含一个字符串Hello World!我们想将它发送到我们的 hello队列。
在RabbitMQ中,消息永远不能直接发送到队列,它总是需要通过exchange。但是,让我们不要被细节拖累 - 您可以在本教程的第三部分中阅读有关exchanges更多信息。我们现在需要知道的是如何使用由空字符串标识的默认交换。这种交换是特殊的 - 它允许我们准确地指定消息应该到达哪个队列。需要在routing_key参数中指定队列名称:
channel.basic_publish(exchange = '',
routing_key = 'hello',
body = 'Hello World!')
print(“[x]发送'Hello World!'”)
在退出程序之前,我们需要确保刷新网络缓冲区并将消息实际传递给RabbitMQ。我们可以通过轻轻关闭连接来实现。
connection.close()
发送不起作用!
如果这是您第一次使用RabbitMQ并且没有看到“已发送”消息,那么您可能会感到头疼,想知道可能出现的问题。也许代理是在没有足够的可用磁盘空间的情况下启动的(默认情况下它至少需要200 MB空闲),因此拒绝接受消息。检查代理日志文件以确认并在必要时减少限制。该配置文件文档会告诉你如何设置disk_free_limit。
接收
我们的第二个程序receive.py将从队列接收消息并在屏幕上打印它们。
同样,首先我们需要连接到RabbitMQ服务器。负责连接Rabbit的代码与之前相同。
与以前一样,下一步是确保队列存在。使用queue_declare创建队列是幂等的 - 我们可以根据需要多次运行命令,并且只创建一个命令。
channel.queue_declare(queue = 'hello')
您可能会问为什么我们再次声明队列 - 我们已经在之前的代码中声明了它。如果我们确定队列已经存在,我们可以避免这种情况。例如,如果之前运行了send.py程序。但我们还不确定首先运行哪个程序。在这种情况下,重复在两个程序中重复声明队列是一个好习惯。
列出队列
您可能希望看到RabbitMQ有哪些队列以及它们中有多少消息。您可以使用rabbitmqctl工具(作为特权用户)执行此操作:sudo rabbitmqctl list_queues
在Windows上,省略sudo:
rabbitmqctl.bat list_queues
从队列接收消息更复杂。它通过将回调(callback)函数订阅到队列来工作。每当我们收到消息时,Pika库都会调用此回调(callback)函数。在我们的例子中,此功能将在屏幕上显示消息的内容。
def callback(ch, method, properties, body):
print(" [x] Received %r" % body)
接下来,我们需要告诉RabbitMQ这个特定的回调函数应该从我们的hello队列接收消息:
channel.basic_consume(callback,
queue = 'hello',
no_ack = True)
要使该命令成功,我们必须确保存在我们想要订阅的队列。幸运的是,我们对此有信心 - 我们在上面创建了一个队列 - 使用queue_declare。
该NO_ACK参数将被描述以后。
最后,我们进入一个永不停止的循环,等待数据并在必要时运行回调。
print('[*]等待消息。退出按CTRL + C')
channel.start_consuming()
把它们放在一起
send.py的完整代码:
#!/usr/bin/env python
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='hello')
channel.basic_publish(exchange='',
routing_key='hello',
body='Hello World!')
print(" [x] Sent 'Hello World!'")
connection.close()
receive.py的完整代码:
#!/usr/bin/env python
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='hello')
def callback(ch, method, properties, body):
print(" [x] Received %r" % body)
channel.basic_consume(callback,
queue='hello',
no_ack=True)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()
现在我们可以在终端中试用我们的程序。首先,让我们开始一个消费者,它将持续运行等待交付:
python receive.py
#=> [*]正在等待消息。要退出按CTRL + C
#=> [x]收到'Hello World!'
现在开始运行生产者, 生产者程序在每次运行之后都会停止:
python send.py
#=> [x]发送'Hello World!'
牛逼!我们能够通过RabbitMQ发送第一条消息。您可能已经注意到,receive.py程序不会退出。它将保持准备好接收更多消息,并可能被Ctrl-C中断。
尝试在新终端中再次运行send.py.
我们已经学会了如何从命名队列发送和接收消息。是时候转到第2部分 并构建一个简单的工作队列了。