Python的Flask框架应用调用Redis队列数据的方法


Posted in Python onJune 06, 2016

任务异步化
打开浏览器,输入地址,按下回车,打开了页面。于是一个HTTP请求(request)就由客户端发送到服务器,服务器处理请求,返回响应(response)内容。

我们每天都在浏览网页,发送大大小小的请求给服务器。有时候,服务器接到了请求,会发现他也需要给另外的服务器发送请求,或者服务器也需要做另外一些事情,于是最初们发送的请求就被阻塞了,也就是要等待服务器完成其他的事情。

更多的时候,服务器做的额外事情,并不需要客户端等待,这时候就可以把这些额外的事情异步去做。从事异步任务的工具有很多。主要原理还是处理通知消息,针对通知消息通常采取是队列结构。生产和消费消息进行通信和业务实现。

生产消费与队列
上述异步任务的实现,可以抽象为生产者消费模型。如同一个餐馆,厨师在做饭,吃货在吃饭。如果厨师做了很多,暂时卖不完,厨师就会休息;如果客户很多,厨师马不停蹄的忙碌,客户则需要慢慢等待。实现生产者和消费者的方式用很多,下面使用Python标准库Queue写个小例子:

import random
import time
from Queue import Queue
from threading import Thread

queue = Queue(10)

class Producer(Thread):
  def run(self):
    while True:
      elem = random.randrange(9)
      queue.put(elem)
      print "厨师 {} 做了 {} 饭 --- 还剩 {} 饭没卖完".format(self.name, elem, queue.qsize())
      time.sleep(random.random())

class Consumer(Thread):
  def run(self):
    while True:
      elem = queue.get()
      print "吃货{} 吃了 {} 饭 --- 还有 {} 饭可以吃".format(self.name, elem, queue.qsize())
      time.sleep(random.random())

def main():
  for i in range(3):
    p = Producer()
    p.start()
  for i in range(2):
    c = Consumer()
    c.start()

if __name__ == '__main__':
  main()

大概输出如下:

厨师 Thread-1 做了 1 饭 --- 还剩 1 饭没卖完
厨师 Thread-2 做了 8 饭 --- 还剩 2 饭没卖完
厨师 Thread-3 做了 3 饭 --- 还剩 3 饭没卖完
吃货Thread-4 吃了 1 饭 --- 还有 2 饭可以吃
吃货Thread-5 吃了 8 饭 --- 还有 1 饭可以吃
吃货Thread-4 吃了 3 饭 --- 还有 0 饭可以吃
厨师 Thread-1 做了 0 饭 --- 还剩 1 饭没卖完
厨师 Thread-2 做了 0 饭 --- 还剩 2 饭没卖完
厨师 Thread-1 做了 1 饭 --- 还剩 3 饭没卖完
厨师 Thread-1 做了 1 饭 --- 还剩 4 饭没卖完
吃货Thread-4 吃了 0 饭 --- 还有 3 饭可以吃
厨师 Thread-3 做了 3 饭 --- 还剩 4 饭没卖完
吃货Thread-5 吃了 0 饭 --- 还有 3 饭可以吃
吃货Thread-5 吃了 1 饭 --- 还有 2 饭可以吃
厨师 Thread-2 做了 8 饭 --- 还剩 3 饭没卖完
厨师 Thread-2 做了 8 饭 --- 还剩 4 饭没卖完

Redis 队列
Python内置了一个好用的队列结构。我们也可以是用redis实现类似的操作。并做一个简单的异步任务。

Redis提供了两种方式来作消息队列。一个是使用生产者消费模式模式,另外一个方法就是发布订阅者模式。前者会让一个或者多个客户端监听消息队列,一旦消息到达,消费者马上消费,谁先抢到算谁的,如果队列里没有消息,则消费者继续监听。后者也是一个或多个客户端订阅消息频道,只要发布者发布消息,所有订阅者都能收到消息,订阅者都是ping的。

生产消费模式
主要使用了redis提供的blpop获取队列数据,如果队列没有数据则阻塞等待,也就是监听。

import redis

class Task(object):
  def __init__(self):
    self.rcon = redis.StrictRedis(host='localhost', db=5)
    self.queue = 'task:prodcons:queue'

  def listen_task(self):
    while True:
      task = self.rcon.blpop(self.queue, 0)[1]
      print "Task get", task

if __name__ == '__main__':
  print 'listen task queue'
  Task().listen_task()

发布订阅模式
使用redis的pubsub功能,订阅者订阅频道,发布者发布消息到频道了,频道就是一个消息队列。

import redis


class Task(object):

  def __init__(self):
    self.rcon = redis.StrictRedis(host='localhost', db=5)
    self.ps = self.rcon.pubsub()
    self.ps.subscribe('task:pubsub:channel')

  def listen_task(self):
    for i in self.ps.listen():
      if i['type'] == 'message':
        print "Task get", i['data']

if __name__ == '__main__':
  print 'listen task channel'
  Task().listen_task()

Flask 入口
我们分别实现了两种异步任务的后端服务,直接启动他们,就能监听redis队列或频道的消息了。简单的测试如下:

import redis
import random
import logging
from flask import Flask, redirect

app = Flask(__name__)

rcon = redis.StrictRedis(host='localhost', db=5)
prodcons_queue = 'task:prodcons:queue'
pubsub_channel = 'task:pubsub:channel'

@app.route('/')
def index():

  html = """
<br>
<center><h3>Redis Message Queue</h3>
<br>
<a href="/prodcons">生产消费者模式</a>
<br>
<br>
<a href="/pubsub">发布订阅者模式</a>
</center>
"""
  return html


@app.route('/prodcons')
def prodcons():
  elem = random.randrange(10)
  rcon.lpush(prodcons_queue, elem)
  logging.info("lpush {} -- {}".format(prodcons_queue, elem))
  return redirect('/')

@app.route('/pubsub')
def pubsub():
  ps = rcon.pubsub()
  ps.subscribe(pubsub_channel)
  elem = random.randrange(10)
  rcon.publish(pubsub_channel, elem)
  return redirect('/')

if __name__ == '__main__':
  app.run(debug=True)

启动脚本,使用

siege -c10 -r 5 http://127.0.0.1:5000/prodcons
siege -c10 -r 5 http://127.0.0.1:5000/pubsub

可以分别在监听的脚本输入中看到异步消息。在异步的任务中,可以执行一些耗时间的操作,当然目前这些做法并不知道异步的执行结果,如果需要知道异步的执行结果,可以考虑设计协程任务或者使用一些工具如RQ或者celery等。

Python 相关文章推荐
Python的Django框架下管理站点的基本方法
Jul 17 Python
Python脚本实现自动发带图的微博
Apr 27 Python
详解Python核心对象类型字符串
Feb 11 Python
django中的HTML控件及参数传递方法
Mar 20 Python
Python3.5面向对象程序设计之类的继承和多态详解
Apr 24 Python
Python实用工具FuckIt.py介绍
Jul 02 Python
利用Python进行图像的加法,图像混合(附代码)
Jul 14 Python
对python3中的RE(正则表达式)-详细总结
Jul 23 Python
Python3常用内置方法代码实例
Nov 18 Python
Python生态圈图像格式转换问题(推荐)
Dec 02 Python
python批量生成条形码的示例
Oct 10 Python
Python代码风格与编程习惯重要吗?
Jun 03 Python
Python第三方库的安装方法总结
Jun 06 #Python
在Python程序和Flask框架中使用SQLAlchemy的教程
Jun 06 #Python
Python的socket模块源码中的一些实现要点分析
Jun 06 #Python
深入浅析python定时杀进程
Jun 06 #Python
深入理解python函数递归和生成器
Jun 06 #Python
python下调用pytesseract识别某网站验证码的实现方法
Jun 06 #Python
浅析AST抽象语法树及Python代码实现
Jun 06 #Python
You might like
PHP通过session id 实现session共享和登录验证的代码
2012/06/03 PHP
php批量更改数据库表前缀实现方法
2013/10/26 PHP
Linux下手动编译安装PHP扩展的例子分享
2014/07/15 PHP
php计算两个文件相对路径的方法
2015/03/14 PHP
php实现paypal 授权登录
2015/05/28 PHP
PHP基于反射机制实现插件的可插拔设计详解
2016/11/10 PHP
PHP实现的获取文件mimes类型工具类示例
2018/04/08 PHP
JavaScript入门教程 Cookies
2009/01/31 Javascript
IE6不能修改NAME问题的解决方法
2010/09/03 Javascript
使用js写的一个简易的投票
2013/11/27 Javascript
Node.js(安装,启动,测试)
2014/06/09 Javascript
JavaScript 获取任一float型小数点后两位的小数
2014/06/30 Javascript
JavaScript获取按钮所在form表单id的方法
2015/04/02 Javascript
javascript+ajax实现产品页面加载信息
2015/07/09 Javascript
JavaScript实现获取某个元素相邻兄弟节点的prev与next方法
2016/01/25 Javascript
仅9张思维导图帮你轻松学习Javascript 就这么简单
2016/06/01 Javascript
jQuery Ajax前后端使用JSON进行交互示例
2017/03/17 Javascript
jQuery EasyUI 为Combo,Combobox添加清除值功能的实例
2017/04/13 jQuery
JavaScript常用事件介绍
2019/01/21 Javascript
解决vue单页面应用中动态修改title问题
2019/06/09 Javascript
JS代码屏蔽F12,右键,粘贴,复制,剪切,选中,操作实例
2019/09/17 Javascript
使用PreloadJS加载图片资源的基础方法详解
2020/02/03 Javascript
vue中实现弹出层动画效果的示例代码
2020/09/25 Javascript
如何通过Proxy实现JSBridge模块化封装
2020/10/22 Javascript
通过实例浅析Python对比C语言的编程思想差异
2015/08/30 Python
Python极简代码实现杨辉三角示例代码
2016/11/15 Python
同时安装Python2 &amp; Python3 cmd下版本自由选择的方法
2017/12/09 Python
Python3 SSH远程连接服务器的方法示例
2018/12/29 Python
python GUI库图形界面开发之PyQt5布局控件QHBoxLayout详细使用方法与实例
2020/03/06 Python
python tqdm库的使用
2020/11/30 Python
阿玛尼意大利官网:Armani意大利
2018/10/30 全球购物
黑猩猩商店:The Chimp Store
2020/02/12 全球购物
如何用Lucene索引数据库
2016/02/23 面试题
95%的面试官都会问到的50道Java线程题,附答案
2012/08/03 面试题
市场部业务员岗位职责
2014/04/02 职场文书
房屋买卖定金协议书
2016/03/21 职场文书