介绍

RabbitMQ是一个消息代理。它的工作就是接收和转发消息。你可以把它想像成一个邮局:你把信件放入邮箱,邮递员就会把信件投递到你的收件人处。在这个比喻中,RabbitMQ就扮演着邮箱、邮局以及邮递员的角色。

RabbitMQ和邮局的主要区别在于,它处理纸张,而是接收、存储和发送消息(message)这种二进制数据。

下面是RabbitMQ和消息所涉及到的一些术语。

  • 生产(Producing)的意思就是发送。发送消息的程序就是一个生产者(producer)。我们一般用"P"来表示:

  • 队列(queue)就是存在于RabbitMQ中邮箱的名称。虽然消息的传输经过了RabbitMQ和你的应用程序,但是它只能被存储于队列当中。实质上队列就是个巨大的消息缓冲区,它的大小只受主机内存和硬盘限制。多个生产者(producers)可以把消息发送给同一个队列,同样,多个消费者(consumers)也能够从同一个队列(queue)中获取数据。队列可以绘制成这样(图上是队列的名称):

  • 在这里,消费(Consuming)和接收(receiving)是同一个意思。一个消费者(consumer)就是一个等待获取消息的程序。我们把它绘制为"C":

需要指出的是生产者、消费者、代理需不要待在同一个设备上;事实上大多数应用也确实不在会将他们放在一台机器上。

Hello World!

(使用pika 1.1.0 Python客户端)

接下来我们用Python写两个小程序。一个发送单条消息的生产者(producer)和一个接收消息并将其输出的消费者(consumer)。传递的消息是"Hello World"。

下图中,“P”代表生产者,“C”代表消费者,中间的盒子代表为消费者保留的消息缓冲区,也就是我们的队列。

生产者(producer)把消息发送到一个名为“hello”的队列中。消费者(consumer)从这个队列中获取消息。

RabbitMQ库

RabbitMQ使用的是AMQP 0.9.1协议。这是一个用于消息传递的开放、通用的协议。针对不同编程语言有大量的RabbitMQ客户端可用。在这个系列教程中,RabbitMQ团队推荐使用Pika这个Python客户端。大家可以通过pip这个包管理工具进行安装:

发送

我们第一个程序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)来进行。但是为了不让细节拖累我们的进度,这里我们只需要知道如何使用由空字符串表示的默认交换机即可。如果你想要详细了解交换机,可以查看我们教程的第三部分来获取更多细节。默认交换机比较特别,它允许我们指定消息究竟需要投递到哪个具体的队列中,队列名字需要在routing_key参数中指定。

channel.basic_publish(exchange='',routing_key='hello',body='Hello World!')
print(" [x] Sent 'Hello World!'")

在退出程序之前,我们需要确认网络缓冲已经被刷写、消息已经投递到RabbitMQ。通过安全关闭连接可以做到这一点。

connection.close()

发送不成功!

如果这是你第一次使用RabbitMQ,并且没有看到“Sent”消息出现在屏幕上,你可能会抓耳挠腮不知所以。这也许是因为没有足够的磁盘空间给代理使用所造成的(代理默认需要200MB的空闲空间),所以它才会拒绝接收消息。查看一下代理的日志文件进行确认,如果需要的话也可以减少限制。配置文件文档会告诉你如何更改磁盘空间限制(disk_free_limit)。

接收

我们的第二个程序receive.py,将会从队列中获取消息并将其打印到屏幕上。

这次我们还是需要要先连接到RabbitMQ服务器。连接服务器的代码和之前是一样的。

下一步也和之前一样,我们需要确认队列是存在的。我们可以多次使用queue_declare命令来创建同一个队列,但是只有一个队列会被真正的创建。

channel.queue_declare(queue='hello')

你也许要问: 为什么要重复声明队列呢 —— 我们已经在前面的代码中声明过它了。如果我们确定了队列是已经存在的,那么我们可以不这么做,比如此前预先运行了send.py程序。可是我们并不确定哪个程序会首先运行。这种情况下,在程序中重复将队列重复声明一下是种值得推荐的做法。

列出所有队列

你也许希望查看RabbitMQ中有哪些队列、有多少消息在队列中。此时你可以使用rabbitmqctl工具(使用有权限的用户):

sudo rabbitmqctl list_queues

(在Windows中不需要sudo命令)

rabbitmqctl list_queues

从队列中获取消息相对来说稍显复杂。需要为队列定义一个回调(callback)函数。当我们获取到消息的时候,Pika库就会调用此回调函数。这个回调函数会将接收到的消息内容输出到屏幕上。

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(' [*] Waiting for messages. To exit press 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()

(send.py源码)

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(queue='hello', on_message_callback=callback, auto_ack=True) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()

(receive.py源码)

现在我们可以在终端中尝试一下我们的程序了。

首先我们启动一个消费者,它会持续的运行来等待投递到达。

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。

官方使用示例:Python code for RabbitMQ tutorials

Requirements

To run this code you need to install the pika package version 1.0.0 or later. To install it, run

python -m pip install pika

You may first need to run

easy_install pip

Code

Tutorial one: "Hello World!":

python send.py
python receive.py

Tutorial two: Work Queues:

python new_task.py "A very hard task which takes two seconds.."
python worker.py

Tutorial three: Publish/Subscribe:

python receive_logs.py
python emit_log.py "info: This is the log message"

Tutorial four: Routing:

python receive_logs_direct.py info
python emit_log_direct.py info "The message"

Tutorial five: Topics:

python receive_logs_topic.py "*.rabbit"
python emit_log_topic.py red.rabbit Hello

Tutorial six: RPC:

python rpc_server.py
python rpc_client.py

py文件

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

2. 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(queue='hello', on_message_callback=callback, auto_ack=True) print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

3. new_task.py

#!/usr/bin/env python
import pika
import sys connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel() channel.queue_declare(queue='task_queue', durable=True) message = ' '.join(sys.argv[1:]) or "Hello World!"
channel.basic_publish(
exchange='',
routing_key='task_queue',
body=message,
properties=pika.BasicProperties(
delivery_mode=2, # make message persistent
))
print(" [x] Sent %r" % message)
connection.close()

4.worker.py

#!/usr/bin/env python
import pika
import time connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel() channel.queue_declare(queue='task_queue', durable=True)
print(' [*] Waiting for messages. To exit press CTRL+C') def callback(ch, method, properties, body):
print(" [x] Received %r" % body)
time.sleep(body.count(b'.'))
print(" [x] Done")
ch.basic_ack(delivery_tag=method.delivery_tag) channel.basic_qos(prefetch_count=1)
channel.basic_consume(queue='task_queue', on_message_callback=callback) channel.start_consuming()

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

6.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()

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

8. 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()

9.receive_logs_topic.py

#!/usr/bin/env python
import pika
import sys connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel() channel.exchange_declare(exchange='topic_logs', exchange_type='topic') result = channel.queue_declare(queue='', exclusive=True)
queue_name = result.method.queue binding_keys = sys.argv[1:]
if not binding_keys:
sys.stderr.write("Usage: %s [binding_key]...\n" % sys.argv[0])
sys.exit(1) for binding_key in binding_keys:
channel.queue_bind(
exchange='topic_logs', queue=queue_name, routing_key=binding_key) 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()

10.emit_log_topic.py

#!/usr/bin/env python
import pika
import sys connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel() channel.exchange_declare(exchange='topic_logs', exchange_type='topic') routing_key = sys.argv[1] if len(sys.argv) > 2 else 'anonymous.info'
message = ' '.join(sys.argv[2:]) or 'Hello World!'
channel.basic_publish(
exchange='topic_logs', routing_key=routing_key, body=message)
print(" [x] Sent %r:%r" % (routing_key, message))
connection.close()

11.rpc_server.py

#!/usr/bin/env python
import pika connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.queue_declare(queue='rpc_queue') def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n - 1) + fib(n - 2) def on_request(ch, method, props, body):
n = int(body) print(" [.] fib(%s)" % n)
response = fib(n) ch.basic_publish(exchange='',
routing_key=props.reply_to,
properties=pika.BasicProperties(correlation_id = \
props.correlation_id),
body=str(response))
ch.basic_ack(delivery_tag=method.delivery_tag) channel.basic_qos(prefetch_count=1)
channel.basic_consume(queue='rpc_queue', on_message_callback=on_request) print(" [x] Awaiting RPC requests")
channel.start_consuming()

12 .rpc_client.py

#!/usr/bin/env python
import pika
import uuid class FibonacciRpcClient(object): def __init__(self):
self.connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost')) self.channel = self.connection.channel() result = self.channel.queue_declare(queue='', exclusive=True)
self.callback_queue = result.method.queue self.channel.basic_consume(
queue=self.callback_queue,
on_message_callback=self.on_response,
auto_ack=True) def on_response(self, ch, method, props, body):
if self.corr_id == props.correlation_id:
self.response = body def call(self, n):
self.response = None
self.corr_id = str(uuid.uuid4())
self.channel.basic_publish(
exchange='',
routing_key='rpc_queue',
properties=pika.BasicProperties(
reply_to=self.callback_queue,
correlation_id=self.corr_id,
),
body=str(n))
while self.response is None:
self.connection.process_data_events()
return int(self.response) fibonacci_rpc = FibonacciRpcClient() print(" [x] Requesting fib(30)")
response = fibonacci_rpc.call(30)
print(" [.] Got %r" % response)

Python应用RabbitMQ教程的更多相关文章

  1. python中RabbitMQ的使用(安装和简单教程)

    1,简介 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现的产品,RabbitMQ是一个消息代理,从"生产者"接收消息 ...

  2. python操作rabbitmq操作数据(不错)

    ##一.RabbitMQ 消息队列介绍 RabbitMQ也是消息队列,那RabbitMQ和之前python的Queue有什么区别么? py 消息队列: 线程 queue(同一进程下线程之间进行交互) ...

  3. python 使用 RabbitMQ

    一.RabbitMQ消息队列介绍 RabbitMQ是在两个独立得python程序,或其他语言交互时使用. RabbitMQ:erlang语言 开发的. Python中连接RabbitMQ的模块:pik ...

  4. Python操作RabbitMQ

    RabbitMQ介绍 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现的产品,RabbitMQ是一个消息代理,从“生产者”接收消息并传递消 ...

  5. 用 Python、 RabbitMQ 和 Nameko 实现微服务

    用 Python. RabbitMQ 和 Nameko 实现微服务 原创 07-17 17:57 首页 Linux中国 "微服务是一股新浪潮" - 现如今,将项目拆分成多个独立的. ...

  6. python之RabbitMQ

    一.安装RabbitMQ 1. 安装erlang 1 2 3 4 tar xf otp_src_18.3.tar.gz cd otp_src_18.3 ./configure --prefix=/ma ...

  7. 用命令访问D:\python学习\wendjia教程\aa.py

    用命令访问D:\python学习\wendjia教程\aa.py d:                                -----------切换到D盘 cd python学习\wend ...

  8. Python小白好教程

    提供一些Python的基础教程. Crossin的编程教师:网址:http://crossincode.com/home/ 廖雪峰的官方网站 网址:http://www.liaoxuefeng.com ...

  9. 七牛云存储Python SDK使用教程 - 上传策略详解

    文 七牛云存储Python SDK使用教程 - 上传策略详解 七牛云存储 python-sdk 七牛云存储教程 jemygraw 2015年01月04日发布 推荐 1 推荐 收藏 2 收藏,2.7k  ...

随机推荐

  1. springMVC中的ModelAndView说明

    ModelAndView 类别就如其名称所示,是代表了Spring Web MVC程式中呈现画面时所使用Model资料物件与View资料物件,由于Java程式中一次只能返回一个物件,所以ModelAn ...

  2. shell编程-定时删除(30天)文件

    1.创建shell touch /opt/auto-del-30-days-ago.sh chmod +x auto-del-30-days-ago.sh 2.编辑shell脚本: vi auto-d ...

  3. linux下无root源码安装软件

    先进入源码文件夹下指定安装路径 ./configure --prefix=/public/home/ztu/usr/samtools 编译 make 安装 make install 写入环境变量 vi ...

  4. node版本如何升级

    爬坑后的结论:window系统升级node只能到node官网下载window安装包来覆盖之前的node. 以下为我的爬坑之路: 今天安装了vue cli 3,使用命令时报: You are using ...

  5. Python关于%matplotlib inline

    Python关于%matplotlib inline %matplotlib inline 是一个魔法函数(Magic Functions).官方给出的定义是:IPython有一组预先定义好的所谓的魔 ...

  6. leetcode常见算法与数据结构汇总

    leetcode刷题之后,很多问题老是记忆不深刻,因此特意开此帖: 一.对做过题目的总结: 二.对一些方法精妙未能领会透彻的代码汇总,进行时常学习: 三.总结面试笔试常见题目,并讨论最优解法及各种解法 ...

  7. Oracle 变量 之 define variable declare 用法及区别

    Oracle 变量 之 define variable declare 用法及区别 Table of Contents 1. 扯蛋 2. define和accept 3. variable 3.1. ...

  8. HashMap 的实现原理解析(转载)

    HashMap 概述 HashMap 是基于哈希表的 Map 接口的非同步实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.此类不保证映射的顺序,特别是它不保证该顺序恒久不 ...

  9. IDEA离线更新

    因为在hosts文件添加以下dns添加 0.0.0.0 account.jetbrains.com 0.0.0.0 www.jetbrains.com 导致IDEA自动更新失败.手工下载回更新文件IU ...

  10. yum安装etcd集群

       前一篇文章介绍了如何yum安装简单的kubernetes集群,其中etcd是单点部署.本篇我们来搭建etcd集群,方便日后搭建kubernetes HA集群架构. 1,环境配置说明 etcd1 ...