Python+Pika+RabbitMQ环境部署及实现工作队列的实例教程


Posted in Python onJune 29, 2016

rabbitmq中文翻译的话,主要还是mq字母上:Message Queue,即消息队列的意思。前面还有个rabbit单词,就是兔子的意思,和python语言叫python一样,老外还是蛮幽默的。rabbitmq服务类似于mysql、apache服务,只是提供的功能不一样。rabbimq是用来提供发送消息的服务,可以用在不同的应用程序之间进行通信。

安装rabbitmq
先来安装下rabbitmq,在ubuntu 12.04下可以直接通过apt-get安装:

sudo apt-get install rabbitmq-server

安装好后,rabbitmq服务就已经启动好了。接下来看下python编写Hello World!的实例。实例的内容就是从send.py发送“Hello World!”到rabbitmq,receive.py从rabbitmq接收send.py发送的信息。

Python+Pika+RabbitMQ环境部署及实现工作队列的实例教程

其中P表示produce,生产者的意思,也可以称为发送者,实例中表现为send.py;C表示consumer,消费者的意思,也可以称为接收者,实例中表现为receive.py;中间红色的表示队列的意思,实例中表现为hello队列。

python使用rabbitmq服务,可以使用现成的类库pika、txAMQP或者py-amqplib,这里选择了pika。

安装pika

安装pika可以使用pip来进行安装,pip是python的软件管理包,如果没有安装,可以通过apt-get安装

sudo apt-get install python-pip

通过pip安装pika:

sudo pip install pika

send.py代码

连接到rabbitmq服务器,因为是在本地测试,所以就用localhost就可以了。

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

声明消息队列,消息将在这个队列中进行传递。如果将消息发送到不存在的队列,rabbitmq将会自动清除这些消息。

channel.queue_declare(queue='hello')

发送消息到上面声明的hello队列,其中exchange表示交换器,能精确指定消息应该发送到哪个队列,routing_key设置为队列的名称,body就是发送的内容,具体发送细节暂时先不关注。

channel.basic_publish(exchange='', routing_key='hello', body='Hello World!')

关闭连接

connection.close()

完整代码

#!/usr/bin/env python
#coding=utf8
import pika
 
connection = pika.BlockingConnection(pika.ConnectionParameters(
        '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()

先来执行下这个程序,执行成功的话,rabbitmqctl应该成功增加了hello队列,并且队列里应该有一条信息,用rabbitmqctl命令来查看下

rabbitmqctl list_queues

在笔者的电脑上输出如下信息:

Python+Pika+RabbitMQ环境部署及实现工作队列的实例教程

确实有一个hello队列,并且队列里有一条信息。接下来用receive.py来获取队列里的信息。

receive.py代码

和send.py的前面两个步骤一样,都是要先连接服务器,然后声明消息的队列,这里就不再贴同样代码了。

接收消息更为复杂一些,需要定义一个回调函数来处理,这边的回调函数就是将信息打印出来。

def callback(ch, method, properties, body):
  print "Received %r" % (body,)

告诉rabbitmq使用callback来接收信息

channel.basic_consume(callback, queue='hello', no_ack=True)

开始接收信息,并进入阻塞状态,队列里有信息才会调用callback进行处理。按ctrl+c退出。

channel.start_consuming()

完整代码

#!/usr/bin/env python
#coding=utf8
import pika
 
connection = pika.BlockingConnection(pika.ConnectionParameters(
        '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()

执行程序,就能够接收到队列hello里的消息Hello World!,然后打印在屏幕上。换一个终端,再次执行send.py,可以看到receive.py这边会再次接收到信息。

工作队列示例

1.准备工作(Preparation)

在实例程序中,用new_task.py来模拟任务分配者, worker.py来模拟工作者。

修改send.py,从命令行参数里接收信息,并发送

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

修改receive.py的回调函数。

import time
 
def callback(ch, method, properties, body):
  print " [x] Received %r" % (body,)
  time.sleep( body.count('.') )
  print " [x] Done"

这边先打开两个终端,都运行worker.py,处于监听状态,这边就相当于两个工作者。打开第三个终端,运行new_task.py

$ python new_task.py First message.
$ python new_task.py Second message..
$ python new_task.py Third message...
$ python new_task.py Fourth message....
$ python new_task.py Fifth message.....
观察worker.py接收到任务,其中一个工作者接收到3个任务 :
$ python worker.py
 [*] Waiting for messages. To exit press CTRL+C
 [x] Received 'First message.'
 [x] Received 'Third message...'
 [x] Received 'Fifth message.....'

另外一个工作者接收到2个任务 :

$ python worker.py
 [*] Waiting for messages. To exit press CTRL+C
 [x] Received 'Second message..'
 [x] Received 'Fourth message....'

从上面来看,每个工作者,都会依次分配到任务。那么如果一个工作者,在处理任务的时候挂掉,这个任务就没有完成,应当交由其他工作者处理。所以应当有一种机制,当一个工作者完成任务时,会反馈消息。

2.消息确认(Message acknowledgment)

消息确认就是当工作者完成任务后,会反馈给rabbitmq。修改worker.py中的回调函数:

def callback(ch, method, properties, body):
  print " [x] Received %r" % (body,)
  time.sleep(5)
  print " [x] Done"
  ch.basic_ack(delivery_tag = method.delivery_tag)

这边停顿5秒,可以方便ctrl+c退出。

去除no_ack=True参数或者设置为False也可以。

channel.basic_consume(callback, queue='hello', no_ack=False)

用这个代码运行,即使其中一个工作者ctrl+c退出后,正在执行的任务也不会丢失,rabbitmq会将任务重新分配给其他工作者。

3.消息持久化存储(Message durability)

虽然有了消息反馈机制,但是如果rabbitmq自身挂掉的话,那么任务还是会丢失。所以需要将任务持久化存储起来。声明持久化存储:

channel.queue_declare(queue='hello', durable=True)

但是这个程序会执行错误,因为hello这个队列已经存在,并且是非持久化的,rabbitmq不允许使用不同的参数来重新定义存在的队列。重新定义一个队列:

channel.queue_declare(queue='task_queue', durable=True)

在发送任务的时候,用delivery_mode=2来标记任务为持久化存储:

channel.basic_publish(exchange='',
           routing_key="task_queue",
           body=message,
           properties=pika.BasicProperties(
             delivery_mode = 2, # make message persistent
           ))

4.公平调度(Fair dispatch)

上面实例中,虽然每个工作者是依次分配到任务,但是每个任务不一定一样。可能有的任务比较重,执行时间比较久;有的任务比较轻,执行时间比较短。如果能公平调度就最好了,使用basic_qos设置prefetch_count=1,使得rabbitmq不会在同一时间给工作者分配多个任务,即只有工作者完成任务之后,才会再次接收到任务。

channel.basic_qos(prefetch_count=1)

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()
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('.') )
  print " [x] Done"
  ch.basic_ack(delivery_tag = method.delivery_tag)
 
channel.basic_qos(prefetch_count=1)
channel.basic_consume(callback,
           queue='task_queue')
 
channel.start_consuming()
Python 相关文章推荐
详解在Python和IPython中使用Docker
Apr 28 Python
python实现爬虫统计学校BBS男女比例之数据处理(三)
Dec 31 Python
python实现按行切分文本文件的方法
Apr 18 Python
python绘制双柱形图代码实例
Dec 14 Python
python自动发送测试报告邮件功能的实现
Jan 22 Python
python 为什么说eval要慎用
Mar 26 Python
python实现抽奖小程序
Apr 15 Python
wxPython绘图模块wxPyPlot实现数据可视化
Nov 19 Python
python网络编程:socketserver的基本使用方法实例分析
Apr 09 Python
如何查看Django ORM执行的SQL语句的实现
Apr 20 Python
浅谈keras中的目标函数和优化函数MSE用法
Jun 10 Python
OpenCV-Python实现油画效果的实例
Jun 08 Python
Python的消息队列包SnakeMQ使用初探
Jun 29 #Python
Python中线程的MQ消息队列实现以及消息队列的优点解析
Jun 29 #Python
深入理解Python中装饰器的用法
Jun 28 #Python
Python中的迭代器与生成器高级用法解析
Jun 28 #Python
Python设计足球联赛赛程表程序的思路与简单实现示例
Jun 28 #Python
详解Python中heapq模块的用法
Jun 28 #Python
Python中operator模块的操作符使用示例总结
Jun 28 #Python
You might like
php表单习惯用的正则表达式
2017/10/11 PHP
在TP5数据库中四个字段实现无限分类的示例
2019/10/18 PHP
弹出广告特效代码(一个IP只弹出一次)
2007/05/11 Javascript
基于jquery的blockui插件显示弹出层
2011/04/14 Javascript
用JS判断IE版本的代码 超管用!
2011/08/09 Javascript
jquery+css+ul模拟列表菜单具体实现思路
2013/04/15 Javascript
JavaScript中的类与实例实现方法
2015/01/23 Javascript
js实现带按钮的上下滚动效果
2015/05/12 Javascript
使用javaScript动态加载Js文件和Css文件
2015/10/24 Javascript
浅谈javascript运算符——条件,逗号,赋值,()和void运算符
2016/07/15 Javascript
详解AngularJS中ng-src指令的使用
2016/09/07 Javascript
全面解析node 表单的图片上传
2016/11/21 Javascript
Vue.js中的图片引用路径的方式
2017/07/28 Javascript
Vue项目部署在Spring Boot出现页面空白问题的解决方案
2018/11/26 Javascript
使用react render props实现倒计时的示例代码
2018/12/06 Javascript
小程序关于请求同步的总结
2019/05/05 Javascript
[01:31:22]DOTA2-DPC中国联赛定级赛 LBZS vs Magma BO3第二场 1月10日
2021/03/11 DOTA
Python异常学习笔记
2015/02/03 Python
利用Python开发实现简单的记事本
2016/11/15 Python
python+pyqt实现右下角弹出框
2017/10/26 Python
Python使用get_text()方法从大段html中提取文本的实例
2019/08/27 Python
Python数据分析模块pandas用法详解
2019/09/04 Python
Python基于字典实现switch case函数调用
2020/07/22 Python
最简单的matplotlib安装教程(小白)
2020/07/28 Python
Python爬虫进阶之爬取某视频并下载的实现
2020/12/08 Python
CSS3哪些新特性值得称赞
2016/03/02 HTML / CSS
HTML5触摸事件实现移动端简易进度条的实现方法
2018/05/04 HTML / CSS
法国家具及室内配件店:home24
2017/01/21 全球购物
群众路线教育实践活动方案
2014/02/02 职场文书
写求职信要注意什么问题
2014/04/12 职场文书
学校标语大全
2014/06/19 职场文书
2014年变电站工作总结
2014/12/19 职场文书
博士导师推荐信
2015/03/25 职场文书
三方合作意向书范本
2015/05/09 职场文书
花木兰观后感
2015/06/10 职场文书
Docker下安装Oracle19c
2022/04/13 Servers