利用Python学习RabbitMQ消息队列


Posted in Python onNovember 30, 2015

RabbitMQ可以当做一个消息代理,它的核心原理非常简单:即接收和发送消息,可以把它想象成一个邮局:我们把信件放入邮箱,邮递员就会把信件投递到你的收件人处,RabbitMQ就是一个邮箱、邮局、投递员功能综合体,整个过程就是:邮箱接收信件,邮局转发信件,投递员投递信件到达收件人处。

RabbitMQ和邮局的主要区别就是RabbitMQ接收、存储和发送的是二进制数据----消息。

rabbitmq基本管理命令:

一步启动Erlang node和Rabbit应用:sudo rabbitmq-server

在后台启动Rabbit node:sudo rabbitmq-server -detached

关闭整个节点(包括应用):sudo rabbitmqctl stop

add_user <UserName> <Password>
delete_user <UserName>
change_password <UserName> <NewPassword>
list_users
add_vhost <VHostPath>
delete_vhost <VHostPath>
list_vhosts
set_permissions [-p <VHostPath>] <UserName> <Regexp> <Regexp> <Regexp>
clear_permissions [-p <VHostPath>] <UserName>
list_permissions [-p <VHostPath>]
list_user_permissions <UserName>
list_queues [-p <VHostPath>] [<QueueInfoItem> ...]
list_exchanges [-p <VHostPath>] [<ExchangeInfoItem> ...]
list_bindings [-p <VHostPath>]
list_connections [<ConnectionInfoItem> ...]

Demo:

producer.py

#!/usr/bin/env python
 # -*- coding: utf_ -*-
 # Date: 年月日
 # Author:蔚蓝行
 # 博客 http://www.cnblogs.com/duanv/
 import pika
 import sys
 #创建连接connection到localhost
 con = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
 #创建虚拟连接channel
 cha = con.channel()
 #创建队列anheng,durable参数为真时,队列将持久化;exclusive为真时,建立临时队列
 result=cha.queue_declare(queue='anheng',durable=True,exclusive=False)
 #创建名为yanfa,类型为fanout的exchange,其他类型还有direct和topic,如果指定durable为真,exchange将持久化
 cha.exchange_declare(durable=False,
           exchange='yanfa',
           type='direct',)
 #绑定exchange和queue,result.method.queue获取的是队列名称
 cha.queue_bind(exchange='yanfa', 
        queue=result.method.queue,
        routing_key='',) 
 #公平分发,使每个consumer在同一时间最多处理一个message,收到ack前,不会分配新的message
 cha.basic_qos(prefetch_count=)
 #发送信息到队列‘anheng'
 message = ' '.join(sys.argv[:])
 #消息持久化指定delivery_mode=;
 cha.basic_publish(exchange='',
          routing_key='anheng',
          body=message,
          properties=pika.BasicProperties(
           delivery_mode = ,
         ))
 print '[x] Sent %r' % (message,)
 #关闭连接
 con.close()

consumer.py

#!/usr/bin/env python
 # -*- coding: utf_ -*-
 # Date: 年月日
 # Author:蔚蓝行
 # 博客 http://www.cnblogs.com/duanv/
 import pika
 #建立连接connection到localhost
 con = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
 #创建虚拟连接channel
 cha = con.channel()
 #创建队列anheng
 result=cha.queue_declare(queue='anheng',durable=True)
 #创建名为yanfa,类型为fanout的交换机,其他类型还有direct和topic
 cha.exchange_declare(durable=False,
           exchange='yanfa', 
           type='direct',)
 #绑定exchange和queue,result.method.queue获取的是队列名称
 cha.queue_bind(exchange='yanfa',
        queue=result.method.queue,
        routing_key='',)
 #公平分发,使每个consumer在同一时间最多处理一个message,收到ack前,不会分配新的message
 cha.basic_qos(prefetch_count=)
 print ' [*] Waiting for messages. To exit press CTRL+C'
 #定义回调函数
 def callback(ch, method, properties, body):
   print " [x] Received %r" % (body,)
   ch.basic_ack(delivery_tag = method.delivery_tag)
 cha.basic_consume(callback,
          queue='anheng',
          no_ack=False,)
 cha.start_consuming()

一、概念:

Connection: 一个TCP的连接。Producer和Consumer都是通过TCP连接到RabbitMQ Server的。程序的起始处就是建立这个TCP连接。

Channels: 虚拟连接。建立在上述的TCP连接中。数据流动都是在Channel中进行的。一般情况是程序起始建立TCP连接,第二步就是建立这个Channel。

二、队列:

首先建立一个Connection,然后建立Channels,在channel上建立队列

建立时指定durable参数为真,队列将持久化;指定exclusive为真,队列为临时队列,关闭consumer后该队列将不再存在,一般情况下建立临时队列并不指定队列名称,rabbitmq将随机起名,通过result.method.queue来获取队列名:

result = channel.queue_declare(exclusive=True)

result.method.queue

区别:durable是队列持久化与否,如果为真,队列将在rabbitmq服务重启后仍存在,如果为假,rabbitmq服务重启前不会消失,与consumer关闭与否无关;

而exclusive是建立临时队列,当consumer关闭后,该队列就会被删除

三、exchange和bind

Exchange中durable参数指定exchange是否持久化,exchange参数指定exchange名称,type指定exchange类型。Exchange类型有direct,fanout和topic。

Bind是将exchange与queue进行关联,exchange参数和queue参数分别指定要进行bind的exchange和queue,routing_key为可选参数。

Exchange的三种模式:

Direct:

任何发送到Direct Exchange的消息都会被转发到routing_key中指定的Queue

1.一般情况可以使用rabbitMQ自带的Exchange:””(该Exchange的名字为空字符串);

2.这种模式下不需要将Exchange进行任何绑定(bind)操作;

3.消息传递时需要一个“routing_key”,可以简单的理解为要发送到的队列名字;

4.如果vhost中不存在routing_key中指定的队列名,则该消息会被抛弃。

Demo中虽然声明了一个exchange='yanfa'和queue='anheng'的bind,但是在后面发送消息时并没有使用该exchange和bind,而是采用了direct的模式,没有指定exchange,而是指定了routing_key的名称为队列名,消息将发送到指定队列。

如果一个exchange 声明为direct,并且bind中指定了routing_key,那么发送消息时需要同时指明该exchange和routing_key.

Fanout:

任何发送到Fanout Exchange的消息都会被转发到与该Exchange绑定(Binding)的所有Queue上

1.可以理解为路由表的模式

2.这种模式不需要routing_key

3.这种模式需要提前将Exchange与Queue进行绑定,一个Exchange可以绑定多个Queue,一个Queue可以同多个Exchange进行绑定。

4.如果接受到消息的Exchange没有与任何Queue绑定,则消息会被抛弃。

Demo中创建了一个将一个exchange和一个queue进行fanout类型的bind.但是发送信息时没有用到它,如果要用到它,只要在发送消息时指定该exchange的名称即可,该exchange就会将消息发送到所有和它bind的队列中。在fanout模式下,指定的routing_key是无效的 。

Topic:

任何发送到Topic Exchange的消息都会被转发到所有关心routing_key中指定话题的Queue上

1.这种模式较为复杂,简单来说,就是每个队列都有其关心的主题,所有的消息都带有一个“标题”(routing_key),Exchange会将消息转发到所有关注主题能与routing_key模糊匹配的队列。

2.这种模式需要routing_key,也许要提前绑定Exchange与Queue。

3.在进行绑定时,要提供一个该队列关心的主题,如“#.log.#”表示该队列关心所有涉及log的消息(一个routing_key为”MQ.log.error”的消息会被转发到该队列)。

4.“#”表示0个或若干个关键字,“*”表示一个关键字。如“log.*”能与“log.warn”匹配,无法与“log.warn.timeout”匹配;但是“log.#”能与上述两者匹配。

5.同样,如果Exchange没有发现能够与routing_key匹配的Queue,则会抛弃此消息。

四、任务分发

1.Rabbitmq的任务是循环分发的,如果开启两个consumer,producer发送的信息是轮流发送到两个consume的。

2.在producer端使用cha.basic_publish()来发送消息,其中body参数就是要发送的消息,properties=pika.BasicProperties(delivery_mode = 2,)启用消息持久化,可以防止RabbitMQ Server 重启或者crash引起的数据丢失。

3.在接收端使用cha.basic_consume()无限循环监听,如果设置no-ack参数为真,每次Consumer接到数据后,而不管是否处理完成,RabbitMQ Server会立即把这个Message标记为完成,然后从queue中删除了。为了保证数据不被丢失,RabbitMQ支持消息确认机制,即acknowledgments。为了保证数据能被正确处理而不仅仅是被Consumer收到,那么我们不能采用no-ack。而应该是在处理完数据后发送ack。

在处理数据后发送的ack,就是告诉RabbitMQ数据已经被接收,处理完成,RabbitMQ可以去安全的删除它了。如果Consumer退出了但是没有发送ack,那么RabbitMQ就会把这个Message发送到下一个Consumer。这样就保证了在Consumer异常退出的情况下数据也不会丢失。

这里并没有用到超时机制。RabbitMQ仅仅通过Consumer的连接中断来确认该Message并没有被正确处理。也就是说,RabbitMQ给了Consumer足够长的时间来做数据处理。

Demo的callback方法中ch.basic_ack(delivery_tag = method.delivery_tag)告诉rabbitmq消息已经正确处理。如果没有这条代码,Consumer退出时,Message会重新分发。然后RabbitMQ会占用越来越多的内存,由于RabbitMQ会长时间运行,因此这个“内存泄漏”是致命的。去调试这种错误,可以通过一下命令打印un-acked Messages:

sudo rabbitmqctl list_queues name messages_ready messages_unacknowledged

4.公平分发:设置cha.basic_qos(prefetch_count=1),这样RabbitMQ就会使得每个Consumer在同一个时间点最多处理一个Message。换句话说,在接收到该Consumer的ack前,他它不会将新的Message分发给它。

五、注意:

生产者和消费者都应该声明建立队列,网上教程上说第二次创建如果参数和第一次不一样,那么该操作虽然成功,但是queue的属性并不会被修改。

可能因为版本问题,在我的测试中如果第二次声明建立的队列属性和第一次不完全相同,将报类似这种错406, "PRECONDITION_FAILED - parameters for queue 'anheng' in vhost '/' not equivalent"

如果是exchange第二次创建属性不同,将报这种错406, "PRECONDITION_FAILED - cannot redeclare exchange 'yanfa' in vhost '/' with different type, durable, internal or autodelete value"

如果第一次声明建立队列也出现这个错误,说明之前存在名字相同的队列且本次声明的某些属性和之前声明不同,可通过命令sudo rabbitmqctl list_queues查看当前有哪些队列。解决方法是声明建立另一名称的队列或删除原有队列,如果原有队列是非持久化的,可通过重启rabbitmq服务删除原有队列,如果原有队列是持久化的,只能删除它所在的vhost,然后再重建vhost,再设置vhost的权限(先确认该vhost中没有其他有用队列)。

sudo rabbitmqctl delete_vhost /
sudo rabbitmqctl add_vhost /
sudo rabbitmqctl set_permissions -p / username '.*' '.*' '.*'

以上内容是小编给大家介绍的利用Python学习RabbitMQ消息队列,希望大家喜欢。

Python 相关文章推荐
跟老齐学Python之网站的结构
Oct 24 Python
Python获取linux主机ip的简单实现方法
Apr 18 Python
python类的继承实例详解
Mar 30 Python
Python图像处理之颜色的定义与使用分析
Jan 03 Python
python脚本开机自启的实现方法
Jun 28 Python
详解Python打包分发工具setuptools
Aug 05 Python
利用Tensorflow构建和训练自己的CNN来做简单的验证码识别方式
Jan 20 Python
计算Python Numpy向量之间的欧氏距离实例
May 22 Python
Pycharm学生免费专业版安装教程的方法步骤
Sep 24 Python
Python高阶函数与装饰器函数的深入讲解
Nov 10 Python
python爬取招聘要求等信息实例
Nov 20 Python
还在手动盖楼抽奖?教你用Python实现自动评论盖楼抽奖(一)
Jun 07 Python
MySQL中表的复制以及大型数据表的备份教程
Nov 25 #Python
python基础知识小结之集合
Nov 25 #Python
python 多线程实现检测服务器在线情况
Nov 25 #Python
Python中time模块与datetime模块在使用中的不同之处
Nov 24 #Python
简单解决Python文件中文编码问题
Nov 22 #Python
Python制作简单的网页爬虫
Nov 22 #Python
Python编程中使用Pillow来处理图像的基础教程
Nov 20 #Python
You might like
PHPWind 发帖回帖Api PHP版打包下载
2010/02/08 PHP
PHP对象Object的概念 介绍
2012/06/14 PHP
php实现的返回数据格式化类实例
2014/09/22 PHP
PHP实现的mysql读写分离操作示例
2018/05/22 PHP
Windows上php5.6操作mongodb数据库示例【配置、连接、获取实例】
2019/02/13 PHP
jquery控制listbox中项的移动并排序
2009/11/12 Javascript
jQuery获取地址栏参数插件(模仿C#)
2010/10/26 Javascript
解析瀑布流布局:JS+绝对定位的实现
2013/05/08 Javascript
JS中实现replaceAll的方法(实例代码)
2013/11/12 Javascript
jQuery .tmpl() 用法示例介绍
2014/08/21 Javascript
js实现使用鼠标拖拽切换图片的方法
2015/05/04 Javascript
jquery实现表单输入时提示文字滑动向上效果
2015/08/10 Javascript
全国省市二级联动下拉菜单 js版
2016/05/10 Javascript
js 数字、字符串、布尔值的转换方法(必看)
2017/04/07 Javascript
Vue集成Iframe页面的方法示例
2017/12/12 Javascript
响应式框架Bootstrap栅格系统的实例
2017/12/19 Javascript
vue中的数据绑定原理的实现
2018/07/02 Javascript
vue 使用element-ui中的Notification自定义按钮并实现关闭功能及如何处理多个通知
2019/08/17 Javascript
vue点击Dashboard不同内容 跳转到同一表格的实例
2020/11/13 Javascript
[03:38]2014DOTA2西雅图国际邀请赛 VG战队巡礼
2014/07/07 DOTA
Python 不同对象比较大小示例探讨
2014/08/21 Python
Python中类型关系和继承关系实例详解
2015/05/25 Python
Python实现针对json中某个关键字段进行排序操作示例
2018/12/25 Python
Python实现线性判别分析(LDA)的MATLAB方式
2019/12/09 Python
python os模块在系统管理中的应用
2020/06/22 Python
css3实现书本翻页效果的示例代码
2021/03/08 HTML / CSS
企业管理毕业生求职信
2014/03/11 职场文书
公司捐款倡议书
2014/05/14 职场文书
2015年教师党员公开承诺书
2015/01/22 职场文书
中学生自我评价范文
2015/03/03 职场文书
2016庆祝教师节新闻稿
2015/11/25 职场文书
为什么在foreach循环中JAVA集合不能添加或删除元素
2021/06/11 Java/Android
CSS实现九宫格布局(自适应)的示例代码
2022/02/12 HTML / CSS
Python实现自动玩连连看的脚本分享
2022/04/04 Python
docker-compose部署Yapi的方法
2022/04/08 Servers
GoFrame基于性能测试得知grpool使用场景
2022/06/21 Golang