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爬虫抓取豆瓣电影TOP100及用户头像的方法
Jan 20 Python
浅谈Python爬取网页的编码处理
Nov 04 Python
python实现员工管理系统
Jan 11 Python
Python实现的拟合二元一次函数功能示例【基于scipy模块】
May 15 Python
对PyTorch torch.stack的实例讲解
Jul 30 Python
Python提取转移文件夹内所有.jpg文件并查看每一帧的方法
Jun 27 Python
python写日志文件操作类与应用示例
Jul 01 Python
画pytorch模型图,以及参数计算的方法
Aug 17 Python
Python查找不限层级Json数据中某个key或者value的路径方式
Feb 27 Python
python中sklearn的pipeline模块实例详解
May 21 Python
TensorFlow keras卷积神经网络 添加L2正则化方式
May 22 Python
python爬虫多次请求超时的几种重试方法(6种)
Dec 01 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
如何做到多笔资料的同步
2006/10/09 PHP
学习php设计模式 php实现合成模式(composite)
2015/12/08 PHP
Laravel使用scout集成elasticsearch做全文搜索的实现方法
2018/11/30 PHP
PHP内部实现打乱字符串顺序函数str_shuffle的方法
2019/02/14 PHP
Yii框架实现对数据库的CURD操作示例
2019/09/03 PHP
jQuery 第二课 操作包装集元素代码
2010/03/14 Javascript
Firefox中autocomplete=&quot;off&quot; 设置不起作用Bug的解决方法
2011/03/25 Javascript
8款非常棒的响应式jQuery 幻灯片插件推荐
2012/02/02 Javascript
简易js代码实现计算器操作
2013/04/15 Javascript
基于JavaScript 类的使用详解
2013/05/07 Javascript
jQuery bxCarousel实现图片滚动切换效果示例代码
2013/05/15 Javascript
JQuery表格内容过滤的实现方法
2013/07/05 Javascript
jquery左边浮动到一定位置时显示返回顶部按钮
2014/06/05 Javascript
详解JavaScript中的Unescape()和String() 函数
2015/11/09 Javascript
jQuery+CSS实现一个侧滑导航菜单代码
2016/05/09 Javascript
jQuery实现调整表格单列顺序完整实例
2016/06/20 Javascript
JavaScript表单验证的两种实现方法
2017/02/11 Javascript
JS打开摄像头并截图上传示例
2017/02/18 Javascript
js中的面向对象入门
2017/03/06 Javascript
JavaScript中如何判断一个值的类型
2017/09/15 Javascript
Angular中点击li标签实现更改颜色的核心代码
2017/12/08 Javascript
JS实现select选中option触发事件操作示例
2018/07/13 Javascript
Vue3.0结合bootstrap创建多页面应用
2019/05/28 Javascript
[41:37]DOTA2北京网鱼队选拔赛——冲击职业之路
2015/04/13 DOTA
[53:15]Newbee vs Pain 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
Python编程之黑板上排列组合,你舍得解开吗
2017/10/30 Python
Python实现串口通信(pyserial)过程解析
2019/09/25 Python
Django 404、500页面全局配置知识点详解
2020/03/10 Python
Pandas中DataFrame交换列顺序的方法实现
2020/12/14 Python
利用css3实现的简单的鼠标悬停按钮
2014/11/04 HTML / CSS
JS原生实现轮播图的几种方法
2021/03/23 Javascript
工商管理专业学生的自我评价
2013/10/01 职场文书
计算机学生的自我评价分享
2014/02/18 职场文书
校优秀毕业生主要事迹
2014/05/26 职场文书
男方婚前保证书
2015/02/28 职场文书
python中使用 unittest.TestCase单元测试的用例详解
2021/08/30 Python