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在linux中输出带颜色的文字的方法
Jun 19 Python
老生常谈python的私有公有属性(必看篇)
Jun 09 Python
Python入门之三角函数全解【收藏】
Nov 08 Python
Python File readlines() 使用方法
Mar 19 Python
基于Django与ajax之间的json传输方法
May 29 Python
Python重新加载模块的实现方法
Oct 16 Python
详解Python3迁移接口变化采坑记
Oct 11 Python
Python 网络编程之UDP发送接收数据功能示例【基于socket套接字】
Oct 11 Python
python实现超市管理系统(后台管理)
Oct 25 Python
python中的RSA加密与解密实例解析
Nov 18 Python
TensorFlow2.X使用图片制作简单的数据集训练模型
Apr 08 Python
python编写函数注意事项总结
Mar 29 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开启gzip页面压缩实例代码
2010/03/11 PHP
PHP中ini_set与ini_get用法实例
2014/11/04 PHP
php实现的mongodb操作类
2015/05/28 PHP
php 7新特性之类型申明详解
2017/06/06 PHP
PHP实现限制IP访问及提交次数的方法详解
2017/07/17 PHP
PHP/ThinkPHP实现批量打包下载文件的方法示例
2017/07/31 PHP
Extjs在exlipse中设置自动提示的方法
2010/04/07 Javascript
jQuery 获取对象 基本选择与层级
2010/05/31 Javascript
javascript for循环从入门到偏门(效率优化+奇特用法)
2012/08/01 Javascript
JS是按值传递还是按引用传递
2015/01/30 Javascript
Google 地图叠加层实例讲解
2016/08/06 Javascript
form+iframe解决跨域上传文件的方法
2016/11/18 Javascript
微信小程序模板之分页滑动栏
2017/02/10 Javascript
JS实现常见的查找、排序、去重算法示例
2018/05/21 Javascript
JS基于封装函数实现的表格分页完整示例
2018/06/26 Javascript
vue elementUI table表格数据 滚动懒加载的实现方法
2019/04/04 Javascript
JavaScript ECMA-262-3 深入解析(二):变量对象实例详解
2020/04/25 Javascript
Vue2.0 $set()的正确使用详解
2020/07/28 Javascript
JavaScript原生数组函数实例汇总
2020/10/14 Javascript
解决VUE 在IE下出现ReferenceError: Promise未定义的问题
2020/11/07 Javascript
[02:44]DOTA2英雄基础教程 魅惑魔女
2014/01/07 DOTA
[08:54]《一刀刀一天》之DOTA全时刻18:十九支奔赴西雅图队伍全部出炉
2014/06/04 DOTA
[09:40]DAC2018 4.5 SOLO赛 MidOne vs Miracle
2018/04/06 DOTA
Python 爬虫的工具列表大全
2016/01/31 Python
从零开始学Python第八周:详解网络编程基础(socket)
2016/12/14 Python
python距离测量的方法
2018/03/06 Python
解决django接口无法通过ip进行访问的问题
2020/03/27 Python
Python截图并保存的具体实例
2021/01/14 Python
一组SQL面试题
2016/02/15 面试题
当当网软件测试笔试题
2015/11/24 面试题
幼儿园保教管理制度
2014/02/03 职场文书
地理教师岗位职责
2014/03/16 职场文书
绿色学校实施方案
2014/03/31 职场文书
新员工试用期自我鉴定
2014/04/17 职场文书
民主生活会汇报材料
2014/12/15 职场文书
事业单位个人总结
2015/02/12 职场文书