Python的Tornado框架实现异步非阻塞访问数据库的示例


Posted in Python onJune 30, 2016

tornado即是一个http非阻塞服务器, 就要用起来, 我们将用到tornado框架 ,mongodb数据库 以及motor(mongodb的异步驱动).来简单实现tornado的非阻塞功能.

其他环境支持的下载与安装

1.安装mongodb

$ sudo apt-get install update
$ sudo apt-get install mongodb

2.安装motor

$ pip install motor

非阻塞

# conf.py

import os
import motor
from handlers import index, auth

BASE_DIR = os.path.join(__file__)

handlers = [
  (r'^/$', index.IndexHandler),
  (r'^/auth/register$', auth.RegisterHandler),
  (r'^/auth/login$', auth.LoginHandler),
]

settings = dict(
  debug = True,
  template_path = os.path.join(BASE_DIR, 'templates'),
  static_path = os.path.join(BASE_DIR, 'static'),
)

client = motor.MotorClient("127.0.0.1")
db = client.meet

首先在配置文件中连接数据库, client.db_name中 db_name就是数据库的名称

# handlers/__init__.py
class BaseHandler(tornado.web.RequestHandler, TemplateRendering):
  def initialite(self):
    ...

  @property
  def db(self):
    return self.application.db

添加db()并使用property装饰,像属性一样访问数据库.

# auth.py

import os 
import time 
import tornado.web
from tornado import gen
from . import BaseHandler

class RegisterHandler(BaseHandler):
  def get(self):
    self.render_html('register.html')

  @tornado.web.asynchronous
  @gen.coroutine
  def post(self):
    username = self.get_argument('username', None)
    email = self.get_argument('email', None)
    password = self.get_argument('password', None)

    data = {
      'username': username,
      'email': email,
      'password': password,
      'timestamp': time.time() * 1000,
    }

    if username and email:
      yield self.db.user.insert(data)
    self.redirect('/')

class LoginHandler(BaseHandler):
  
  @tornado.web.asynchronous
  @gen.coroutine
  def get(self):
    username = self.get_argument('useranme')
    user = yield self.db.user.find_one({'username': username})
    self.render_html('login.html', user=user)

@gen.coroutine装饰使函数非阻塞, 返回一个生成器, 而不用在使用回调函数. motor也通过yield 实现异步(不然还得返回一个回调函数). 其实这个例子反映不了阻塞问题关键是时间太短.
我们修改一下代码

# 之前
yield self.db.user.insert(data)

# 之后
yield tornado.gen.Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + 10)

这里通过tornado.ioloop.IOLoop.instance().add_timeout阻塞应用, 这是time.sleep的非阻塞实现, 如果这里使用time.sleep因为是tornado是单线程会阻塞整个应用所以别的handler也无法访问.
可以看到我在注册页面注册后,在阻塞期间点击/auth/login直接就访问了login页完成非阻塞.

异步下的redirect问题
在使用tornado的时候常常遇到一些问题, 特将遇到的问题和解决的方法写出来(这里的感谢一下帮我解答疑惑的pythonista们)

1.问题

我想要实现一个注册用户功能, web框架使用tornado数据库使用mongodb但在注册时出现Exception redirect的错误. 现贴下代码:

class Register(BaseHandler):
  def get(self):
    self.render_html('register.html')

  @tornado.web.aynchronous
  @gen.coroutine
  def post(self):
    username = self.get_argument('username')
    email = self.get_argument('email')
    password = self.get_argument('password')
    captcha = self.get_argument('captcha')

    _verify_username = yield self.db.user.find_one({'username': username})
    if _verify_username:
      self.flash(u'用户名已存在', 'error')
      self.redirect('/auth/register')

    _verify_email = yield self.db.user.find_one({'email': email})
    if _verify_email:
      self.flash(u'邮箱已注册', 'error')
      self.redirect('/auth/register')

    if captcha and captcha == self.get_secure_cookie('captcha').replace(' ',''):
      self.flash(u'验证码输入正确', 'info')
    else:
      self.flash(u'验证码输入错误', 'error')
      self.redirect('/auth/register')

    password = haslib.md5(password + self.settings['site']).hexdigest()

    profile = {'headimg': '', 'site': '', 'job': '', 'signature':'',
          'github': '', 'description': ''}
    user_profile = yield self.db.profile.insert(profile)
    user = {'username': username, 'email': email, 'password': password,
        'timestamp': time.time(), 'profile_id': str(user_profile)}

    yield self.db.user.insert(user)
    self.set_secure_cookie('user', username)
    self.redirect('/')

本想如果用户验证码输入出错就跳转到注册页面, 但问题是验证码出错也会继续执行一下代码. 虽然在self.redirect后加上self.finish会终止代码,但是因为self.redirect 函数内已有self.finish所以出现了两次报出异常终止的代码.
因为以上原因代码不会被终结, 验证码出错用户还是会注册.

2.解决方案

return self.redirect('/auth/register')


self.redirect('/auth/register')
return

(1)segmentdefault中热心用户rsj217给出的答案
self.finish 会关掉请求, 因为@tornado.web.aynchronous告诉tornado会一直等待请求(长链接). self.redirect等于设置了response的headers的location属性.

(2)segmentdefault中热心用户依云给出的答案
self.finish当然不会跳出函数, 不然请求结束之后还想做些事情怎么办呢.

3.总结

因为错把self.finish当做跳出函数出现了以上的问题

  • self.redirect会在request.headers 里设置location用于跳转
  • self.finish会关掉请求, 但不会跳出函数
Python 相关文章推荐
解决python2.7用pip安装包时出现错误的问题
Jan 23 Python
浅谈python中的数字类型与处理工具
Aug 02 Python
Python tkinter实现的图片移动碰撞动画效果【附源码下载】
Jan 04 Python
Python在groupby分组后提取指定位置记录方法
Apr 20 Python
Python3.6通过自带的urllib通过get或post方法请求url的实例
May 10 Python
用Python实现大文本文件切割的方法
Jan 12 Python
python3使用matplotlib绘制散点图
Mar 19 Python
python pandas获取csv指定行 列的操作方法
Jul 12 Python
python numpy之np.random的随机数函数使用介绍
Oct 06 Python
python开发实例之python使用Websocket库开发简单聊天工具实例详解(python+Websocket+JS)
Mar 18 Python
Pytorch之Tensor和Numpy之间的转换的实现方法
Sep 03 Python
Python实战之实现康威生命游戏
Apr 26 Python
Python的Tornado框架实现图片上传及图片大小修改功能
Jun 30 #Python
举例讲解Python中metaclass元类的创建与使用
Jun 30 #Python
在Python中定义和使用抽象类的方法
Jun 30 #Python
Python中functools模块的常用函数解析
Jun 30 #Python
深入浅析Python中join 和 split详解(推荐)
Jun 30 #Python
Python列出一个文件夹及其子目录的所有文件
Jun 30 #Python
django之常用命令详解
Jun 30 #Python
You might like
深入理解PHP之数组(遍历顺序)  Laruence原创
2012/06/13 PHP
基于php split()函数的用法详解
2013/06/05 PHP
PHP 安全检测代码片段(分享)
2013/07/05 PHP
PHP以json或xml格式返回请求数据的方法
2018/05/31 PHP
PHP观察者模式示例【Laravel框架中有用到】
2018/06/15 PHP
PHP应用跨时区功能的实现方法
2019/03/21 PHP
javaScript函数中执行C#代码中的函数方法总结
2013/08/07 Javascript
JavaScript使用focus()设置焦点失败的解决方法
2014/09/03 Javascript
javascript实现根据3原色制作颜色选择器的方法
2015/07/17 Javascript
第一次接触神奇的Bootstrap网格系统
2016/07/27 Javascript
原生js实现放大镜
2017/02/20 Javascript
基于javascript中的typeof和类型判断(详解)
2017/10/27 Javascript
代码详解JS操作剪贴板
2018/02/11 Javascript
使用Vue-cli3.0创建的项目 如何发布npm包
2019/10/10 Javascript
vue data引入本地图片的两种方式小结
2019/11/13 Javascript
python实现的防DDoS脚本
2011/02/08 Python
Python设计模式之抽象工厂模式
2016/08/25 Python
Python2与python3中 for 循环语句基础与实例分析
2017/11/20 Python
Python使用re模块正则提取字符串中括号内的内容示例
2018/06/01 Python
用python建立两个Y轴的XY曲线图方法
2019/07/08 Python
django-crontab实现服务端的定时任务的示例代码
2020/02/17 Python
Numpy中np.max的用法及np.maximum区别
2020/11/27 Python
Python实现区域填充的示例代码
2021/02/03 Python
CSS3的新特性介绍
2008/10/31 HTML / CSS
荷兰优雅女装网上商店:Heine
2016/11/14 全球购物
美国一家运动专业鞋类零售商:Warehouse Shoe Sale(WSS)
2018/03/28 全球购物
世界排名第一的万圣节服装店:Spirit Halloween
2018/10/16 全球购物
非功能性需求都包括哪些方面
2013/10/29 面试题
毕业生自荐书模版
2014/01/04 职场文书
一年级小学生评语
2014/04/22 职场文书
祖国在我心中演讲稿300字
2014/05/04 职场文书
投标售后服务承诺书
2015/04/29 职场文书
感恩父母主题班会
2015/08/12 职场文书
Django migrate报错的解决方案
2021/05/20 Python
PyTorch 实现L2正则化以及Dropout的操作
2021/05/27 Python
只用50行Python代码爬取网络美女高清图片
2021/06/02 Python