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 相关文章推荐
python基础教程之基本数据类型和变量声明介绍
Aug 29 Python
python私有属性和方法实例分析
Jan 15 Python
python基础教程之匿名函数lambda
Jan 17 Python
Python中with及contextlib的用法详解
Jun 08 Python
python模块smtplib实现纯文本邮件发送功能
May 22 Python
Python数据分析matplotlib设置多个子图的间距方法
Aug 03 Python
Pytoch之torchvision.transforms图像变换实例
Dec 30 Python
浅谈python中频繁的print到底能浪费多长时间
Feb 21 Python
Python sklearn库实现PCA教程(以鸢尾花分类为例)
Feb 24 Python
python plt可视化——打印特殊符号和制作图例代码
Apr 17 Python
利用python爬取有道词典的方法
Dec 08 Python
Python天气语音播报小助手
Sep 25 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字符转义相关函数小结(php下的转义字符串)
2007/04/12 PHP
《PHP编程最快明白》第七讲:php图片验证码与缩略图
2010/11/01 PHP
解析PHP实现下载文件的两种方法
2013/07/05 PHP
设置php页面编码的两种方法示例介绍
2014/03/03 PHP
PHP开发框架kohana中处理ajax请求的例子
2014/07/14 PHP
Chrome Web App开发小结
2014/09/04 PHP
yii使用activeFileField控件实现上传文件与图片的方法
2015/12/28 PHP
Prototype 学习 工具函数学习($w,$F方法)
2009/07/12 Javascript
在chrome中window.onload事件的一些问题
2010/03/01 Javascript
JavaScript学习笔记之获取当前目录的实现代码
2010/12/14 Javascript
jquery里的each使用方法详解
2010/12/22 Javascript
jQuery使用ajaxSubmit()提交表单示例
2014/04/04 Javascript
js闭包实现按秒计数
2015/04/23 Javascript
js实现浮动在网页右侧的简洁QQ在线客服代码
2015/09/04 Javascript
jQuery操作基本控件方法实例分析
2015/12/31 Javascript
全面理解JavaScript中的继承(必看)
2016/06/16 Javascript
全面了解函数声明与函数表达式、变量提升
2016/08/09 Javascript
使用AngularJS 跨站请求如何解决jsonp请求问题
2017/01/16 Javascript
js中作用域的实例解析
2017/03/16 Javascript
webpack3之loader全解析
2017/10/26 Javascript
Python中一些不为人知的基础技巧总结
2018/05/19 Python
python pandas模块基础学习详解
2019/07/03 Python
Python爬虫获取豆瓣电影并写入excel
2020/07/31 Python
python报错TypeError: ‘NoneType‘ object is not subscriptable的解决方法
2020/11/05 Python
python中pop()函数的语法与实例
2020/12/01 Python
详解python 条件语句和while循环的实例代码
2020/12/28 Python
3D动画《斗罗大陆》上线当日播放过亿
2021/03/16 国漫
html5中如何将图片的绝对路径转换成文件对象
2018/01/11 HTML / CSS
儿科护士自我鉴定
2013/10/14 职场文书
业务部主管岗位职责
2014/01/29 职场文书
绩效考核实施方案
2014/03/18 职场文书
党员公开承诺书范文
2014/03/25 职场文书
党员廉洁自律承诺书
2014/05/26 职场文书
销售团队口号大全
2014/06/06 职场文书
学习十八届四中全会精神思想汇报
2014/10/23 职场文书
党员倡议书
2015/01/19 职场文书