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中实现贪婪排名算法的教程
Apr 17 Python
Python删除windows垃圾文件的方法
Jul 14 Python
教大家使用Python SqlAlchemy
Feb 12 Python
Python读取word文本操作详解
Jan 22 Python
使用python读取txt文件的内容,并删除重复的行数方法
Apr 18 Python
python实现括号匹配的思路详解
Aug 23 Python
python集合比较(交集,并集,差集)方法详解
Sep 13 Python
对python xlrd读取datetime类型数据的方法详解
Dec 26 Python
Django命名URL和反向解析URL实现解析
Aug 09 Python
Python pandas.DataFrame 找出有空值的行
Sep 09 Python
浅谈Python中的生成器和迭代器
Jun 19 Python
python 利用jieba.analyse进行 关键词提取
Dec 17 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 CKEditor 上传图片实现代码
2009/11/06 PHP
PHP,ASP.JAVA,JAVA代码格式化工具整理
2010/06/15 PHP
php源代码安装常见错误与解决办法分享
2013/05/28 PHP
PHP+Ajax验证码验证用户登录
2016/07/20 PHP
js中的escape及unescape函数的php实现代码
2007/09/04 Javascript
js 操作css实现代码
2009/06/11 Javascript
基于jQuery捕获超链接事件进行局部刷新代码
2012/05/10 Javascript
Jquery uploadify图片上传插件无法上传的解决方法
2013/12/16 Javascript
Jquery搜索父元素操作方法
2015/02/10 Javascript
Yii2使用Bootbox插件实现自定义弹窗
2015/04/02 Javascript
四种参数传递的形式——URL,超链接,js,form表单
2015/07/24 Javascript
jQuery+CSS3实现3D立方体旋转效果
2015/11/10 Javascript
jQuery移动web开发中的页面初始化与加载事件
2015/12/03 Javascript
JavaScript注入漏洞的原理及防范(详解)
2016/12/04 Javascript
使用Require.js封装原生js轮播图的实现代码
2017/06/15 Javascript
vue实现登陆登出的实现示例
2017/09/15 Javascript
基于vue实现一个禅道主页拖拽效果
2019/05/27 Javascript
详解js location.href和window.open的几种用法和区别
2019/12/02 Javascript
ng-alain的sf如何自定义部件的流程
2020/06/12 Javascript
Python新手在作用域方面经常容易碰到的问题
2015/04/03 Python
Python中的hypot()方法使用简介
2015/05/18 Python
python的mysqldb安装步骤详解
2017/08/14 Python
浅谈关于Python3中venv虚拟环境
2018/08/01 Python
Python爬虫实现获取动态gif格式搞笑图片的方法示例
2018/12/24 Python
python使用adbapi实现MySQL数据库的异步存储
2019/03/19 Python
python实现邮件自动发送
2019/08/10 Python
Python虚拟环境venv用法详解
2020/05/25 Python
linux面试题参考答案(6)
2016/06/23 面试题
中年人生感言
2014/02/04 职场文书
党的群众路线教育实践活动批评与自我批评
2014/02/16 职场文书
导师评语大全
2014/04/26 职场文书
公司委托书格式范文
2014/10/09 职场文书
2014光棍节大学生联谊活动方案
2014/10/10 职场文书
社区母亲节活动总结
2015/02/10 职场文书
2019年公司卫生管理制度样本
2019/08/21 职场文书
仅用一句SQL更新整张表的涨跌幅、涨跌率的解决方案
2021/05/06 MySQL