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是否能连接成功的方法
Jul 30 Python
使用Python搭建虚拟环境的配置方法
Feb 28 Python
Python的numpy库中将矩阵转换为列表等函数的方法
Apr 04 Python
将tensorflow的ckpt模型存储为npy的实例
Jul 09 Python
Python实现获取当前目录下文件名代码详解
Mar 10 Python
Pandas缺失值2种处理方式代码实例
Jun 13 Python
python设置中文界面实例方法
Oct 27 Python
python list等分并从等分的子集中随机选取一个数
Nov 16 Python
用基于python的appium爬取b站直播消费记录
Apr 17 Python
Django操作cookie的实现
May 26 Python
Python爬虫之自动爬取某车之家各车销售数据
Jun 02 Python
彻底弄懂Python中的回调函数(callback)
Jun 25 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
第五节 克隆 [5]
2006/10/09 PHP
PHP在获取指定目录下的目录,在获取的目录下面再创建文件,多平台
2011/08/03 PHP
php简单的会话类代码
2011/08/08 PHP
PHP文件操作实现代码分享
2011/09/01 PHP
PHP 正则表达式之正则处理函数小结(preg_match,preg_match_all,preg_replace,preg_split)
2012/10/05 PHP
php中防止SQL注入的最佳解决方法
2013/04/25 PHP
PHP CURL获取cookies模拟登录的方法
2013/11/04 PHP
php 数组随机取值的简单实例
2016/05/23 PHP
PHP中类型转换 ,常量,系统常量,魔术常量的详解
2017/10/26 PHP
thinkphp5 加载静态资源路径与常量的方法
2017/12/24 PHP
Prototype Selector对象学习
2009/07/23 Javascript
JavaScript 原型学习总结
2010/10/29 Javascript
jsTree 基于JQuery的排序节点 Bug
2011/07/26 Javascript
jQuery页面滚动浮动层智能定位实例代码
2011/08/23 Javascript
轻松创建nodejs服务器(7):阻塞操作的实现
2014/12/18 NodeJs
JS实现仿腾讯微博无刷新删除微博效果代码
2015/10/16 Javascript
jquery无限级联下拉菜单简单实例演示
2015/11/23 Javascript
js异步上传多张图片插件的使用方法
2018/10/22 Javascript
vue移动端屏幕适配详解
2019/04/30 Javascript
[01:31]DOTA2上海特级锦标赛 SECRET战队完整宣传片
2016/03/16 DOTA
Python 实现引用其他.py文件中的类和类的方法
2018/04/29 Python
Python实现查找二叉搜索树第k大的节点功能示例
2019/01/24 Python
PyQT实现菜单中的复制,全选和清空的功能的方法
2019/06/17 Python
Python实现微信机器人的方法
2019/09/06 Python
python实现的登录与提交表单数据功能示例
2019/09/25 Python
下载官网python并安装的步骤详解
2019/10/12 Python
Python安装依赖(包)模块方法详解
2020/02/14 Python
Python matplotlib可视化实例解析
2020/06/01 Python
基于pytorch中的Sequential用法说明
2020/06/24 Python
FILA斐乐中国官方商城:意大利运动品牌
2017/01/25 全球购物
意大利中国电子产品购物网站:Geekmall.com
2019/09/30 全球购物
SQL数据库笔试题
2016/03/08 面试题
服务之星获奖感言
2014/01/21 职场文书
《诺贝尔》教学反思
2014/02/17 职场文书
销售督导岗位职责
2015/04/10 职场文书
初中英语教学反思范文
2016/02/15 职场文书