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实现简单的可逆加密程序实例
Mar 05 Python
Python实现发送QQ邮件的封装
Jul 14 Python
Python与R语言的简要对比
Nov 14 Python
Python实现的朴素贝叶斯算法经典示例【测试可用】
Jun 13 Python
Python3内置模块random随机方法小结
Jul 13 Python
python脚本之一键移动自定格式文件方法实例
Sep 02 Python
python 读取更新中的log 或其它文本方式
Dec 24 Python
Pytorch中Tensor与各种图像格式的相互转化详解
Dec 26 Python
Python利用逻辑回归分类实现模板
Feb 15 Python
Python远程linux执行命令实现
Nov 11 Python
Python 实现集合Set的示例
Dec 21 Python
Python实现简单的猜单词
Jun 15 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
我的论坛源代码(二)
2006/10/09 PHP
深入mysql_fetch_row()与mysql_fetch_array()的区别详解
2013/06/05 PHP
yii实现CheckBox复选框在同一行显示的方法
2014/12/03 PHP
Ext JS 4官方文档之三 -- 类体系概述与实践
2012/12/16 Javascript
单击和双击事件的冲突处理示例代码
2014/04/03 Javascript
js实现点击左右按钮轮播图片效果实例
2015/01/29 Javascript
JQuery实现鼠标滚轮滑动到页面节点
2015/07/28 Javascript
基于Flowplayer打造一款免费的WEB视频播放器附源码
2015/09/06 Javascript
JS拉起或下载app的实现代码
2017/02/22 Javascript
解决vue里碰到 $refs 的问题的方法
2017/07/13 Javascript
Angularjs 1.3 中的$parse实例代码
2017/09/14 Javascript
vue首次赋值不触发watch的解决方法
2018/09/11 Javascript
NodeJS服务器实现gzip压缩的示例代码
2018/10/12 NodeJs
微信小程序 授权登录详解(附完整源码)
2019/08/23 Javascript
antd日期选择器禁止选择当天之前的时间操作
2020/10/29 Javascript
[04:54]DOTA2 2017国际邀请赛:上届冠军WINGS采访短片
2017/08/09 DOTA
[01:13:01]2018DOTA2亚洲邀请赛 4.4 淘汰赛 TNC vs VG 第三场
2018/04/05 DOTA
Python模块学习 filecmp 文件比较
2012/08/27 Python
使用beaker让Facebook的Bottle框架支持session功能
2015/04/23 Python
python检测某个变量是否有定义的方法
2015/05/20 Python
python使用pymysql实现操作mysql
2016/09/13 Python
python一键升级所有pip package的方法
2017/01/16 Python
Python通过命令开启http.server服务器的方法
2017/11/04 Python
python爬虫基本知识
2018/03/05 Python
了解不常见但是实用的Python技巧
2019/05/23 Python
python如何从文件读取数据及解析
2019/09/19 Python
python numpy数组复制使用实例解析
2020/01/10 Python
python中查看.db文件中表格的名字及表格中的字段操作
2020/07/07 Python
python绘制高斯曲线
2021/02/19 Python
幼儿园庆六一游园活动方案
2014/01/29 职场文书
家长对老师的感言
2014/03/11 职场文书
党性锻炼的心得体会
2014/09/03 职场文书
大一工商管理职业生涯规划:有梦最美,行动相随
2014/09/18 职场文书
2016年公司中秋节致辞
2015/11/26 职场文书
《角的初步认识》教学反思
2016/02/17 职场文书
Nginx开启Brotli压缩算法实现过程详解
2021/03/31 Servers