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 相关文章推荐
在Django的模型中添加自定义方法的示例
Jul 21 Python
Python中函数参数设置及使用的学习笔记
May 03 Python
TensorFlow神经网络优化策略学习
Mar 09 Python
对pandas replace函数的使用方法小结
May 18 Python
pytorch + visdom 处理简单分类问题的示例
Jun 04 Python
在python中将字符串转为json对象并取值的方法
Dec 31 Python
python中使用 xlwt 操作excel的常见方法与问题
Jan 13 Python
python3.7 openpyxl 删除指定一列或者一行的代码
Oct 08 Python
Python中itertools的用法详解
Feb 07 Python
关于Python字符串显示u...的解决方式
Mar 06 Python
Python Dataframe常见索引方式详解
May 27 Python
使paramiko库执行命令时在给定的时间强制退出功能的实现
Mar 03 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
农民C键的运用技巧
2020/03/04 星际争霸
PHP5 安装方法
2006/10/09 PHP
实用函数10
2007/11/08 PHP
解析smarty 截取字符串函数 truncate的用法介绍
2013/06/20 PHP
js编码之encodeURIComponent使用介绍(asp,php)
2012/03/01 Javascript
js 时间函数应用加、减、比较、格式转换的示例代码
2013/08/23 Javascript
jQuery is()函数用法3例
2014/05/06 Javascript
Node.js插件的正确编写方式
2014/08/03 Javascript
使用jquery菜单插件HoverTree仿京东无限级菜单
2014/12/18 Javascript
node.js 使用ejs模板引擎时后缀换成.html
2015/04/22 Javascript
分享Javascript实用方法二
2015/12/13 Javascript
javascript针对不确定函数的执行方法
2015/12/16 Javascript
AngularJS 避繁就简的路由
2016/07/01 Javascript
AngularJs  unit-testing(单元测试)详解
2016/09/02 Javascript
Vue-resource实现ajax请求和跨域请求示例
2017/02/23 Javascript
不刷新网页就能链接新的js文件方法总结
2020/03/01 Javascript
Python实现同时兼容老版和新版Socket协议的一个简单WebSocket服务器
2014/06/04 Python
Python爬虫抓取代理IP并检验可用性的实例
2018/05/07 Python
实例分析python3实现并发访问水平切分表
2018/09/29 Python
Spring @Enable模块驱动原理及使用实例
2020/06/23 Python
详解python os.path.exists判断文件或文件夹是否存在
2020/11/16 Python
韩国休闲女装品牌网站:ANAIS
2016/08/24 全球购物
中国宠物用品商城:E宠商城
2016/08/27 全球购物
"火柴棍式"程序员面试题
2014/03/16 面试题
数控加工专业毕业生自荐信
2013/09/27 职场文书
青年创业培训欢迎词
2014/01/08 职场文书
敬老文明号事迹材料
2014/01/16 职场文书
四年大学自我鉴定
2014/02/17 职场文书
婚礼秀策划方案
2014/05/19 职场文书
节水标语大全
2014/06/11 职场文书
“四风”查摆问题自我剖析材料
2014/09/27 职场文书
市委常委会班子党的群众路线教育实践活动整改方案
2014/10/25 职场文书
工程质检员岗位职责
2015/04/08 职场文书
小学语文教师竞聘演讲稿范文
2019/08/09 职场文书
pycharm 如何查看某一函数源码的快捷键
2021/05/12 Python
Redis 报错 error:NOAUTH Authentication required
2022/05/15 Redis