简介
RabbitMQ 是一个消息中间件(message broker)。它的核心思想很简单:接收和发送消息。你可以把它想象成一个邮局:当你寄信,把信投到邮箱里时,你确信一定有邮递员把信取走并送到收件人手中。RabbitMQ就可以比作邮箱+邮局+邮递员。
当然,RabbitMQ 和邮局最重要的区别是,它存储、发送的不是信件,而是二进制的数据——消息。
RabbitMQ在使用中有一些术语。
- *“生产者”*其实和“消息发送”意思差不多。一个可以发送消息的程序叫做生产者:
- 一个队列可以比喻作是一个邮箱,包含在RabbitMQ里。虽然消息的传输需要通过RabbitMQ和你的应用程序,但这些消息只能保存在队列中。一个队列是没有什么约束和限制的,只要你愿意它可以存储很多消息,本质上来说它就是一个无穷的缓冲区。多个消息生产者可以向一个队列发送消息,同样多个消费者可以尝试接收一个队列中的消息。下图所示就是一个队列:
- 消费也可以理解为接收。一个接收者就是一个等待接收消息的程序。下图就是一个接受者“C”:
注意:生产者、消费者、中间件不必在同一台机器上。而且在多数应用场景下,他们没有在一台机器上。一个应用也可以同时是生产者和消费者。
Hello World!
(运用Python中的Pika库)
在这个案例中,我们要用Python写两个小程序,一个负责发送一条消息的生产者,和一个负责接收消息并输出的消费者。消息内容是“Hello World”。
在下图中,“P”是生产者,“C”是消费者。中间是一个队列——也就是RabbitMQ为生产者保留信息的信息缓冲区。
生产者向队列发送“hello”,消费者从队列接收消息。
安装Pika库
可通过下面的语句安装:
python -m pip install pika --upgrade
装好Pika库后,就可以写程序了。
Sending
我们的程序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 的步骤。在这里就不赘述,详见第三章。现在我们只需知道默认的 exchange 就是空字符串的形式。这种 exchange 很特别 —— 它允许我们指定消息要去哪个特定的队列。这个队列的名称写在routing_key
参数中:
channel.basic_publish(exchange='',
routing_key='hello',
body='Hello World!')
print(" [x] Sent 'Hello World!'")
在退出程序之前,我们需要确保网络缓冲区已刷新并且消息已传递到RabbitMQ。 我们可以通过关闭连接来实现它。
connection.close()
无法发送!
如果这是你第一次使用RabbitMQ并且你没有看到“Sent”出的消息,可能的原因是中间件没有足够的磁盘空间(默认最少需要200MB的磁盘空间),从而拒绝接收消息。检查中间件(broker)的日志文件,必要时减少一些限制。此文件帮助你如何设置
disk_free_limit
。
Receiving
第二个程序receive.py
将会从队列中接收消息并打印在屏幕上。
我们首先要做的依然是连接RabbitMQ服务器,实现代码同上。
下一步,我们依然是要确保队列存在,用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这个callback
函数应该从 hello 队列接收消息:
channel.basic_consume(queue='hello',
auto_ack=True,
on_message_callback=callback)
为了使该命令成功执行,我们必须确保队列存在。 幸运的是,因为我们之前已经使用queue_declare
创建了队列,我们可以确保队列是存在的。
auto_ack
参数就在之后解释。
最终,我们形成一个无限循环,不断地等待数据并在需要的时候调用callback
函数。
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()
最终代码
send.py
(source)
#!/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
(source)
#!/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(
queue='hello', on_message_callback=callback, auto_ack=True)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()
现在我们可以在终端运行了。 首先,我们运行一个消费者,它将持续运行等待消息:
python receive.py
# => [*] Waiting for messages. To exit press CTRL+C
# => [x] Received 'Hello World!'
接着运行生产者。程序每次运行完成后就会停止:
python send.py
# => [x] Sent 'Hello World!'
太棒了! 我们通过RabbitMQ发送了第一条消息。 你可能注意到,receive.py
程序不会退出。 它随时准备接收其他消息,你可以通过 Ctrl-C 中断。
尝试在新终端中再次运行send.py
。
我们已经学习了如何从一个命名的队列发送和接收消息。