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实现的简单FTP上传下载文件实例
Jun 30 Python
python高手之路python处理excel文件(方法汇总)
Jan 07 Python
使用Python生成随机密码的示例分享
Feb 18 Python
基于Django模板中的数字自增(详解)
Sep 05 Python
详解Python之unittest单元测试代码
Jan 24 Python
python实现桌面壁纸切换功能
Jan 21 Python
Python jieba库用法及实例解析
Nov 04 Python
Python的缺点和劣势分析
Nov 19 Python
Python实现微信好友的数据分析
Dec 16 Python
Python3常见函数range()用法详解
Dec 30 Python
Python综合应用名片管理系统案例详解
Jan 03 Python
一文带你了解Python 四种常见基础爬虫方法介绍
Dec 04 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的ob_start();控制您的浏览器cache!
2007/02/14 PHP
深入PHP获取随机数字和字母的方法详解
2013/06/06 PHP
PHP解析目录路径的3个函数总结
2014/11/18 PHP
Javascript 构造函数 实例分析
2008/11/26 Javascript
javascript小数计算出现近似值的解决办法
2010/02/06 Javascript
js中arguments,caller,callee,apply的用法小结
2014/01/28 Javascript
浅谈JavaScript的Polymer框架中的事件绑定
2015/07/29 Javascript
Node.js的MongoDB驱动Mongoose基本使用教程
2016/03/01 Javascript
浅谈JavaScript 执行环境、作用域及垃圾回收
2016/05/31 Javascript
jq给页面添加覆盖层遮罩的实例
2017/02/16 Javascript
Js apply方法详解
2017/02/16 Javascript
win系统下nodejs环境安装配置
2017/05/04 NodeJs
vuejs实现ready函数加载完之后执行某个函数的方法
2018/08/31 Javascript
Vue通过WebSocket建立长连接的实现代码
2019/11/05 Javascript
Python单例模式实例分析
2015/01/14 Python
python生成IP段的方法
2015/07/07 Python
老生常谈python中的重载
2018/11/11 Python
Python实现的逻辑回归算法示例【附测试csv文件下载】
2018/12/28 Python
django rest framework 实现用户登录认证详解
2019/07/29 Python
wxPython+Matplotlib绘制折线图表
2019/11/19 Python
pytorch实现保证每次运行使用的随机数都相同
2020/02/20 Python
Python 去除字符串中指定字符串
2020/03/05 Python
PIP和conda 更换国内安装源的方法步骤
2020/09/21 Python
简单介绍HTML5中audio标签的使用
2015/09/24 HTML / CSS
Probikekit日本:自行车套件,跑步和铁人三项装备
2017/04/03 全球购物
加热夹克:RAVEAN
2018/10/19 全球购物
蔻驰意大利官网:COACH意大利
2019/01/16 全球购物
世界上最大的艺术社区:SAA
2020/12/30 全球购物
电气工程及其自动化自我评价四篇
2013/09/24 职场文书
英语自荐信常用语句
2013/12/13 职场文书
接受捐赠答谢词
2014/01/27 职场文书
老师的检讨书
2014/02/23 职场文书
暑期社会实践先进个人主要事迹
2014/05/22 职场文书
2014年保险公司工作总结
2014/11/22 职场文书
农贸批发市场管理制度
2015/08/07 职场文书
Vscode中SSH插件如何远程连接Linux
2022/05/02 Servers