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进阶教程之循环相关函数range、enumerate、zip
Aug 30 Python
跟老齐学Python之永远强大的函数
Sep 14 Python
python实现的简单窗口倒计时界面实例
May 05 Python
【Python】Python的urllib模块、urllib2模块批量进行网页下载文件
Nov 19 Python
python机器学习实战之树回归详解
Dec 20 Python
Python操作MySQL数据库的方法
Jun 20 Python
python 简单照相机调用系统摄像头实现方法 pygame
Aug 03 Python
Flask框架学习笔记之表单基础介绍与表单提交方式
Aug 12 Python
python实现WebSocket服务端过程解析
Oct 18 Python
python创建学生管理系统
Nov 22 Python
Python 实现向word(docx)中输出
Feb 13 Python
Python matplotlib安装以及实现简单曲线的绘制
Apr 26 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面向对象全攻略 (一) 面向对象基础知识
2009/09/30 PHP
查找mysql字段中固定字符串并替换的几个方法
2012/09/23 PHP
PHP中的排序函数sort、asort、rsort、krsort、ksort区别分析
2014/08/18 PHP
WordPress主题制作中自定义头部的相关PHP函数解析
2016/01/08 PHP
浅谈PHP eval()函数定义和用法
2016/06/21 PHP
文字幻灯片
2006/06/26 Javascript
用javascript实现给图片加链接
2007/08/15 Javascript
Javascript对象Clone实例分析
2015/06/09 Javascript
js实现仿阿里巴巴城市选择框效果实例
2015/06/24 Javascript
使用JS读取XML文件的方法
2016/11/25 Javascript
Vue中的Vux配置指南
2017/12/08 Javascript
Vue Element使用icon图标教程详解(第三方)
2018/02/07 Javascript
Vue2.0 实现单选互斥的方法
2018/04/13 Javascript
jQuery中图片展示插件highslide.js的简单dom
2018/04/22 jQuery
详解使用VueJS开发项目中的兼容问题
2018/08/02 Javascript
如何在vue里面优雅的解决跨域(路由冲突问题)
2019/01/20 Javascript
jQuery操作动画完整实例分析
2020/01/10 jQuery
微信小程序获取公众号文章列表及显示文章的示例代码
2020/03/10 Javascript
vue-路由精讲 二级路由和三级路由的作用
2020/08/06 Javascript
JavaScript实现页面高亮操作提示和蒙板
2021/01/04 Javascript
[01:23:45]DOTA2-DPC中国联赛 正赛 CDEC vs Dragon BO3 第一场 1月22日
2021/03/11 DOTA
Python中的测试模块unittest和doctest的使用教程
2015/04/14 Python
在Python中使用成员运算符的示例
2015/05/13 Python
微信跳一跳python代码实现
2018/01/05 Python
python 列表递归求和、计数、求最大元素的实例
2018/11/28 Python
django富文本编辑器的实现示例
2019/04/10 Python
浅谈Python 递归算法指归
2019/08/22 Python
Python lambda表达式filter、map、reduce函数用法解析
2019/09/11 Python
HTML5 WebGL 实现民航客机飞行监控系统
2019/07/25 HTML / CSS
AmazeUI 平滑滚动效果的示例代码
2020/08/20 HTML / CSS
REISS美国官网:伦敦最受欢迎的时尚品牌
2019/08/16 全球购物
创业计划书——互联网商机
2014/01/12 职场文书
财务稽核岗位职责
2015/04/13 职场文书
2015年教师节主持词
2015/07/03 职场文书
如何有效防止sql注入的方法
2021/05/25 SQL Server
SQLServer中exists和except用法介绍
2021/12/04 SQL Server