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 25 Python
用Python写王者荣耀刷金币脚本
Dec 21 Python
pip matplotlib报错equired packages can not be built解决
Jan 06 Python
实例分析python3实现并发访问水平切分表
Sep 29 Python
对python 判断数字是否小于0的方法详解
Jan 26 Python
详解pandas的外部数据导入与常用方法
May 01 Python
Python之修改图片像素值的方法
Jul 03 Python
Python 实现取多维数组第n维的前几位
Nov 26 Python
python 正则表达式参数替换实例详解
Jan 17 Python
Python多进程multiprocessing、进程池用法实例分析
Mar 24 Python
Python自动巡检H3C交换机实现过程解析
Aug 14 Python
Python 操作pdf pdfplumber读取PDF写入Exce
Aug 14 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 fgetcsv 定义和用法(附windows与linux下兼容问题)
2012/05/29 PHP
PHP并发多进程处理利器Gearman使用介绍
2016/05/16 PHP
php基于mcrypt_encrypt和mcrypt_decrypt实现字符串加密解密的方法
2016/07/12 PHP
php文件管理基本功能简单操作
2017/01/16 PHP
ToolTips JQEURY插件之简洁小提示框效果
2011/11/19 Javascript
javascript中简单的进制转换代码实例
2013/10/26 Javascript
怎么判断js脚本加载完成
2014/02/28 Javascript
JavaScript学习笔记之基础语法
2015/01/22 Javascript
js文本框输入内容智能提示效果
2015/12/02 Javascript
js中window.open的参数及注意注意事项
2016/07/06 Javascript
jQuery Validate表单验证插件的基本使用方法及功能拓展
2017/01/04 Javascript
详解JS中的快速排序与冒泡
2017/01/10 Javascript
xmlplus组件设计系列之网格(DataGrid)(10)
2017/05/05 Javascript
详解在Vue中如何使用axios跨域访问数据
2017/07/07 Javascript
详解前端路由实现与react-router使用姿势
2017/08/07 Javascript
微信小程序使用image组件显示图片的方法【附源码下载】
2017/12/08 Javascript
Element-ui table中过滤条件变更表格内容的方法
2018/03/02 Javascript
JavaScript 九种跨域方式实现原理
2019/02/11 Javascript
扫微信小程序码实现网站登陆实现解析
2019/08/20 Javascript
Taro小程序自定义顶部导航栏功能的实现
2020/12/17 Javascript
在Python的Flask框架下收发电子邮件的教程
2015/04/21 Python
浅谈Python浅拷贝、深拷贝及引用机制
2016/12/15 Python
详解Python3中setuptools、Pip安装教程
2019/06/18 Python
python获取点击的坐标画图形的方法
2019/07/09 Python
python 哈希表实现简单python字典代码实例
2019/09/27 Python
Python换行与不换行的输出实例
2020/02/19 Python
使用OpenCV去除面积较小的连通域
2020/07/05 Python
使用anaconda安装pytorch的实现步骤
2020/09/03 Python
屈臣氏乌克兰:Watsons UA
2019/10/29 全球购物
2014年党员创先争优承诺书
2014/05/29 职场文书
企业活动策划方案
2014/06/02 职场文书
药店采购员岗位职责
2014/09/30 职场文书
2016年教师节贺卡寄语
2015/12/04 职场文书
幼儿教师继续教育培训心得体会
2016/01/19 职场文书
一文搞懂php的垃圾回收机制
2021/06/18 PHP
2022漫威和DC电影上映作品
2022/04/05 欧美动漫