Python中线程的MQ消息队列实现以及消息队列的优点解析


Posted in Python onJune 29, 2016

“消息队列”是在消息的传输过程中保存消息的容器。消息队列管理器在将消息从它的源中继到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。相信对任何架构或应用来说,消息队列都是一个至关重要的组件,下面是十个理由:

Python的消息队列示例:

1.threading+Queue实现线程队列

#!/usr/bin/env python
 
import Queue
import threading
import time
 
queue = Queue.Queue()
 
class ThreadNum(threading.Thread):
 """没打印一个数字等待1秒,并发打印10个数字需要多少秒?"""
 def __init__(self, queue):
  threading.Thread.__init__(self)
  self.queue = queue
 
 def run(self):
  whileTrue:
   #消费者端,从队列中获取num
   num = self.queue.get()
   print"i'm num %s"%(num)
   time.sleep(1)
   #在完成这项工作之后,使用 queue.task_done() 函数向任务已经完成的队列发送一个信号
   self.queue.task_done()
 
start = time.time()
def main():
 #产生一个 threads pool, 并把消息传递给thread函数进行处理,这里开启10个并发
 for i in range(10):
  t = ThreadNum(queue)
  t.setDaemon(True)
  t.start()
  
 #往队列中填错数据 
 for num in range(10):
   queue.put(num)
 #wait on the queue until everything has been processed
 queue.join()
 
main()
print"Elapsed Time: %s" % (time.time() - start)

运行结果:

i'm num 0
i'm num 1
i'm num 2
i'm num 3
i'm num 4
i'm num 5
i'm num 6
i'm num 7
i'm num 8
i'm num 9
Elapsed Time: 1.01399993896

解读:
具体工作步骤描述如下:
1,创建一个 Queue.Queue() 的实例,然后使用数据对它进行填充。
2,将经过填充数据的实例传递给线程类,后者是通过继承 threading.Thread 的方式创建的。
3,生成守护线程池。
4,每次从队列中取出一个项目,并使用该线程中的数据和 run 方法以执行相应的工作。
5,在完成这项工作之后,使用 queue.task_done() 函数向任务已经完成的队列发送一个信号。
6,对队列执行 join 操作,实际上意味着等到队列为空,再退出主程序。
在使用这个模式时需要注意一点:通过将守护线程设置为 true,程序运行完自动退出。好处是在退出之前,可以对队列执行 join 操作、或者等到队列为空。

2.多个队列
所谓多个队列,一个队列的输出可以作为另一个队列的输入!

#!/usr/bin/env python
import Queue
import threading
import time
 
queue = Queue.Queue()
out_queue = Queue.Queue()
 
class ThreadNum(threading.Thread):
  """bkeep"""
  def __init__(self, queue, out_queue):
    threading.Thread.__init__(self)
    self.queue = queue
    self.out_queue = out_queue
 
  def run(self):
    whileTrue:
      #从队列中取消息
      num = self.queue.get()
      bkeep = num
      
      #将bkeep放入队列中
      self.out_queue.put(bkeep)
 
      #signals to queue job is done
      self.queue.task_done()
 
class PrintLove(threading.Thread):
  """Threaded Url Grab"""
  def __init__(self, out_queue):
    threading.Thread.__init__(self)
    self.out_queue = out_queue
 
  def run(self):
    whileTrue:
      #从队列中获取消息并赋值给bkeep
      bkeep = self.out_queue.get()  
      keke = "I love " + str(bkeep)
      print keke,
      print self.getName()
      time.sleep(1)
 
      #signals to queue job is done
      self.out_queue.task_done()
 
start = time.time()
def main():
  #populate queue with data
  for num in range(10):
    queue.put(num)
    
  #spawn a pool of threads, and pass them queue instance
  for i in range(5):
    t = ThreadNum(queue, out_queue)
    t.setDaemon(True)
    t.start()
 
 
  for i in range(5):
    pl = PrintLove(out_queue)
    pl.setDaemon(True)
    pl.start()
 
  #wait on the queue until everything has been processed
  queue.join()
  out_queue.join()
 
main()
print"Elapsed Time: %s" % (time.time() - start)

运行结果:

I love 0 Thread-6
I love 1 Thread-7
I love 2 Thread-8
I love 3 Thread-9
I love 4 Thread-10
I love 5 Thread-7
I love 6 Thread-6
I love 7 Thread-9
I love 8 Thread-8
I love 9 Thread-10
Elapsed Time: 2.00300002098

 
解读:
ThreadNum 类工作流程
定义队列--->继承threading---->初始化queue---->定义run函数--->get queue中的数据---->处理数据---->put数据到另外一个queue-->发信号告诉queue该条处理完毕
 
main函数工作流程:
--->往自定义queue中扔数据
--->for循环确定启动的线程数---->实例化ThreadNum类---->启动线程并设置守护
--->for循环确定启动的线程数---->实例化PrintLove类--->启动线程并设置为守护
--->等待queue中的消息处理完毕后执行join。即退出主程序。

了解了MQ的大概实现以后,我们来总结一下消息队列的优点
1. 解耦

在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。消息队列在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口。这允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。

2. 冗余

有时在处理数据的时候处理过程会失败。除非数据被持久化,否则将永远丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。在被许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理过程明确的指出该消息已经被处理完毕,确保你的数据被安全的保存直到你使用完毕。

3. 扩展性

因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的;只要另外增加处理过程即可。不需要改变代码、不需要调节参数。扩展就像调大电力按钮一样简单。

4. 灵活性 & 峰值处理能力

当你的应用上了Hacker News的首页,你将发现访问流量攀升到一个不同寻常的水平。在访问量剧增的情况下,你的应用仍然需要继续发挥作用,但是这样的突发流量并不常见;如果为 以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住增长的访问压力,而不是因为超出负荷的请求而完全崩溃。 请查看我们关于峰值处理能力的博客文章了解更多此方面的信息。

5. 可恢复性

当体系的一部分组件失效,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。而这种允许重试或者延后处理请求的能力通常是造就一个略感不便的用户和一个沮丧透顶的用户之间的区别。

6. 送达保证

消息队列提供的冗余机制保证了消息能被实际的处理,只要一个进程读取了该队列即可。在此基础上,IronMQ提供了一个"只送达一次"保证。无论有多少进 程在从队列中领取数据,每一个消息只能被处理一次。这之所以成为可能,是因为获取一个消息只是"预定"了这个消息,暂时把它移出了队列。除非客户端明确的 表示已经处理完了这个消息,否则这个消息会被放回队列中去,在一段可配置的时间之后可再次被处理。

7.排序保证

在许多情况下,数据处理的顺序都很重要。消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。IronMO保证消息浆糊通过FIFO(先进先出)的顺序来处理,因此消息在队列中的位置就是从队列中检索他们的位置。

8.缓冲

在任何重要的系统中,都会有需要不同的处理时间的元素。例如,加载一张图片比应用过滤器花费更少的时间。消息队列通过一个缓冲层来帮助任务最高效率的执行--写入队列的处理会尽可能的快速,而不受从队列读的预备处理的约束。该缓冲有助于控制和优化数据流经过系统的速度。

9. 理解数据流

在一个分布式系统里,要得到一个关于用户操作会用多长时间及其原因的总体印象,是个巨大的挑战。消息系列通过消息被处理的频率,来方便的辅助确定那些表现不佳的处理过程或领域,这些地方的数据流都不够优化。

10. 异步通信

很多时候,你不想也不需要立即处理消息。消息队列提供了异步处理机制,允许你把一个消息放入队列,但并不立即处理它。你想向队列中放入多少消息就放多少,然后在你乐意的时候再去处理它们。

Python 相关文章推荐
使用Python的Bottle框架写一个简单的服务接口的示例
Aug 25 Python
Python selenium 三种等待方式详解(必会)
Sep 15 Python
Python调用C++程序的方法详解
Jan 24 Python
tensorflow实现简单的卷积网络
May 24 Python
Python + selenium + requests实现12306全自动抢票及验证码破解加自动点击功能
Nov 23 Python
Python制作exe文件简单流程
Jan 24 Python
Python命令行click参数用法解析
Dec 19 Python
python pyenv多版本管理工具的使用
Dec 23 Python
python小白切忌乱用表达式
May 29 Python
Pandas之缺失数据的实现
Jan 06 Python
教你如何用Python实现人脸识别(含源代码)
Jun 23 Python
python数字图像处理之对比度与亮度调整示例
Jun 28 Python
深入理解Python中装饰器的用法
Jun 28 #Python
Python中的迭代器与生成器高级用法解析
Jun 28 #Python
Python设计足球联赛赛程表程序的思路与简单实现示例
Jun 28 #Python
详解Python中heapq模块的用法
Jun 28 #Python
Python中operator模块的操作符使用示例总结
Jun 28 #Python
基础的十进制按位运算总结与在Python中的计算示例
Jun 28 #Python
Python中的with语句与上下文管理器学习总结
Jun 28 #Python
You might like
强烈推荐:php.ini中文版(1)
2006/10/09 PHP
php序列化函数serialize() 和 unserialize() 与原生函数对比
2015/05/08 PHP
两种php去除二维数组的重复项方法
2015/11/04 PHP
PHP缓存工具XCache安装与使用方法详解
2018/04/09 PHP
PHP array_shift()用法实例分析
2019/01/07 PHP
laravel 获取某个查询的查询SQL语句方法
2019/10/12 PHP
javascript 出生日期和身份证判断大全
2008/11/13 Javascript
JavaScript中各种编码解码函数的区别和注意事项
2010/08/19 Javascript
JS性能优化笔记搜索整理
2013/08/21 Javascript
jQuery中extend()和fn.extend()方法详解
2015/06/03 Javascript
JavaScript程序开发之JS代码放置的位置
2016/01/15 Javascript
JS中setTimeout的巧妙用法前端函数节流
2016/03/24 Javascript
Node.js中防止错误导致的进程阻塞的方法
2016/08/11 Javascript
浅谈EasyUI常用控件的禁用方法
2016/11/09 Javascript
实例分析浏览器中“JavaScript解析器”的工作原理
2016/12/12 Javascript
详谈jQuery.load()和Jsp的include的区别
2017/04/12 jQuery
使用travis-ci如何持续部署node.js应用详解
2017/07/30 Javascript
修改npm全局安装模式的路径方法
2018/05/15 Javascript
JS实现图片拖拽交换效果
2018/11/30 Javascript
Python文件操作类操作实例详解
2014/07/11 Python
python opencv之分水岭算法示例
2018/02/24 Python
python使用openpyxl库修改excel表格数据方法
2018/05/03 Python
Python实现定时自动关闭的tkinter窗口方法
2019/02/16 Python
Laravel+Dingo/Api 自定义响应的实现
2019/02/17 Python
Django框架 querySet功能解析
2019/09/04 Python
python属于哪种语言
2020/08/16 Python
python使用正则表达式匹配txt特定字符串(有换行)
2020/12/09 Python
html5指南-4.使用Geolocation实现定位功能
2013/01/07 HTML / CSS
九年级语文教学反思
2014/02/04 职场文书
个人培训自我鉴定
2014/03/28 职场文书
2014年实习班主任工作总结
2014/11/08 职场文书
推广普通话的宣传语
2015/07/13 职场文书
公司员工管理制度
2015/08/04 职场文书
2016年“5.12”国际护士节活动总结
2016/04/06 职场文书
使用Java去实现超市会员管理系统
2022/03/18 Java/Android
 分享一个Python 遇到数据库超好用的模块
2022/04/06 Python