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 相关文章推荐
MAC中PyCharm设置python3解释器
Dec 15 Python
在Python中将函数作为另一个函数的参数传入并调用的方法
Jan 22 Python
python连接PostgreSQL数据库的过程详解
Sep 18 Python
python3反转字符串的3种方法(小结)
Nov 07 Python
python 实现按对象传值
Dec 26 Python
将tensorflow.Variable中的某些元素取出组成一个新的矩阵示例
Jan 04 Python
Keras自动下载的数据集/模型存放位置介绍
Jun 19 Python
PyQt5-QDateEdit的简单使用操作
Jul 12 Python
Python爬取某平台短视频的方法
Feb 08 Python
只用40行Python代码就能写出pdf转word小工具
May 31 Python
5行Python代码实现一键批量扣图
Jun 29 Python
再谈python_tkinter弹出对话框创建
Mar 20 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
如何将数据从文本导入到mysql
2006/10/09 PHP
php短址转换实现方法
2015/02/25 PHP
PHP环境搭建(php+Apache+mysql)
2016/11/14 PHP
JS 实现导航栏悬停效果(续)
2013/09/24 Javascript
javascript通过className来获取元素的简单示例代码
2014/01/10 Javascript
js判断上传文件类型判断FileUpload文件类型代码
2014/05/20 Javascript
基于jQuery创建鼠标悬停效果的方法
2015/03/07 Javascript
jQuery找出网页上最高元素的方法
2015/03/20 Javascript
JavaScript实现DIV层拖动及动态增加新层的方法
2015/05/12 Javascript
轻松学习jQuery插件EasyUI EasyUI创建菜单与按钮
2015/11/30 Javascript
基于JS实现无缝滚动思路及代码分享
2016/06/07 Javascript
原生JS实现风箱式demo,并封装了一个运动框架(实例代码)
2016/07/22 Javascript
神级程序员JavaScript300行代码搞定汉字转拼音
2017/05/20 Javascript
解决vue里碰到 $refs 的问题的方法
2017/07/13 Javascript
如何为你的JS项目添加智能提示与类型检查详解
2019/03/12 Javascript
JavaScript常用内置对象用法分析
2019/07/09 Javascript
vue+springboot+element+vue-resource实现文件上传教程
2020/10/21 Javascript
Python3通过Luhn算法快速验证信用卡卡号的方法
2015/05/14 Python
解决Python安装后pip不能用的问题
2018/06/12 Python
对Python使用mfcc的两种方式详解
2019/01/09 Python
Python hashlib模块实例使用详解
2019/12/24 Python
python GUI库图形界面开发之PyQt5中QMainWindow, QWidget以及QDialog的区别和选择
2020/02/26 Python
Python爬虫使用bs4方法实现数据解析
2020/08/25 Python
俄罗斯电子产品在线商店:UltraTrade
2020/01/30 全球购物
俄罗斯游戏商店:Buka
2020/03/01 全球购物
.net笔试题
2014/03/03 面试题
软件测试常见笔试题
2012/02/04 面试题
计算机专业应届毕业生自荐信
2013/09/26 职场文书
电钳专业个人求职信
2014/01/04 职场文书
《蚕姑娘》教学反思
2014/04/15 职场文书
小学生竞选班干部演讲稿
2014/04/24 职场文书
白酒营销策划方案
2014/08/17 职场文书
2014年社区重阳节活动策划方案
2014/09/16 职场文书
公司给客户的感谢信
2015/01/23 职场文书
Spring Boot 整合 Apache Dubbo的示例代码
2021/07/04 Java/Android
解析python中的jsonpath 提取器
2022/01/18 Python