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清除字符串里非数字字符的方法
Jul 02 Python
用matplotlib画等高线图详解
Dec 14 Python
Caffe均值文件mean.binaryproto转mean.npy的方法
Jul 09 Python
django框架自定义用户表操作示例
Aug 07 Python
pygame游戏之旅 添加游戏暂停功能
Nov 21 Python
python 使用while写猜年龄小游戏过程解析
Oct 07 Python
Python 获取命令行参数内容及参数个数的实例
Dec 20 Python
jenkins+python自动化测试持续集成教程
May 12 Python
Keras 切换后端方式(Theano和TensorFlow)
Jun 19 Python
Python实现快速大文件比较代码解析
Sep 04 Python
使用python-cv2实现Harr+Adaboost人脸识别的示例
Oct 27 Python
python 生成正态分布数据,并绘图和解析
Dec 21 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
PHP中的一些常用函数收集
2015/05/26 PHP
php实现的统计字数函数定义与使用示例
2017/07/26 PHP
PHP+MySQL高并发加锁事务处理问题解决方法
2018/04/30 PHP
PHP MVC框架中类的自动加载机制实例分析
2019/09/18 PHP
jquery 实现表单验证功能代码(简洁)
2012/07/03 Javascript
让元素在网页中可拖动示例代码
2013/08/13 Javascript
如何判断元素是否为HTMLElement元素
2013/12/06 Javascript
jquery中对于批量deferred的处理方法
2014/01/22 Javascript
Node.js与PHP、Python的字符处理性能对比
2014/07/06 Javascript
JS合并数组的几种方法及优劣比较
2014/09/19 Javascript
javascript实现无限级select联动菜单
2015/01/02 Javascript
JavaScript基本数据类型及值类型和引用类型
2015/08/25 Javascript
Bootstrap开关(switch)控件学习笔记分享
2016/05/30 Javascript
JS实现图片局部放大或缩小的方法
2016/08/20 Javascript
jQuery progressbar通过Ajax请求实现后台进度实时功能
2016/10/11 Javascript
jQuery基本筛选选择器实例代码
2017/02/06 Javascript
详解如何探测小程序返回到webview页面
2019/05/14 Javascript
[44:40]Spirit vs Navi Supermajor小组赛 A组败者组第一轮 BO3 第一场 6.2
2018/06/03 DOTA
Python实现复杂对象转JSON的方法示例
2017/06/22 Python
儿童python练习实例
2018/05/27 Python
简单了解python字符串前面加r,u的含义
2019/12/26 Python
多个python文件调用logging模块报错误
2020/02/12 Python
python try...finally...的实现方法
2020/11/25 Python
法国在线药房:DoctiPharma
2020/10/21 全球购物
意大利包包和行李箱销售网站:Bagaglio.it
2021/03/02 全球购物
债务纠纷委托书
2014/08/30 职场文书
中国文明网向国旗敬礼活动精彩寄语2014
2014/09/27 职场文书
党员个人自我剖析材料
2014/10/08 职场文书
入党介绍人考察意见
2015/06/01 职场文书
三傻大闹宝莱坞观后感
2015/06/03 职场文书
医者仁心观后感
2015/06/17 职场文书
交通安全温馨提示语
2015/07/14 职场文书
phpQuery解析HTML乱码问题(补充官网未列出的乱码解决方案)
2021/04/01 PHP
python tqdm用法及实例详解
2021/06/16 Python
使用PostGIS完成两点间的河流轨迹及流经长度的计算(推荐)
2022/01/18 PostgreSQL