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函数的参数的默认值所引发的问题的原因
Mar 30 Python
详解Python中用于计算指数的exp()方法
May 14 Python
python中kmeans聚类实现代码
Feb 23 Python
python切片及sys.argv[]用法详解
May 25 Python
django认证系统实现自定义权限管理的方法
Aug 28 Python
python实现简单成绩录入系统
Sep 19 Python
如何理解Python中包的引入
May 29 Python
python 实现朴素贝叶斯算法的示例
Sep 30 Python
python数据库批量插入数据的实现(executemany的使用)
Apr 30 Python
python 常用的异步框架汇总整理
Jun 18 Python
详解如何用Python实现感知器算法
Jun 18 Python
Django基础CBV装饰器和中间件
Mar 22 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的类 功能齐全的发送邮件类
2006/10/09 PHP
利用php递归实现无限分类 格式化数组的详解
2013/06/08 PHP
php数组删除元素示例
2014/03/21 PHP
yii2超好用的日期组件和时间组件
2016/05/05 PHP
PHP检查端口是否可以被绑定的方法示例
2018/08/09 PHP
初试jQuery EasyUI 使用介绍
2010/04/01 Javascript
jQuery组件easyui基本布局实现代码
2016/08/25 Javascript
Node.js connect ECONNREFUSED错误解决办法
2016/09/15 Javascript
详解Javascript中DOM的范围
2017/02/13 Javascript
vue+mousemove实现鼠标拖动功能(拖动过快失效问题解决方法)
2018/08/24 Javascript
jQuery实现基本淡入淡出效果的方法详解
2018/09/05 jQuery
详解Vue.directive 自定义指令
2019/03/27 Javascript
echarts大屏字体自适应的方法步骤
2019/07/12 Javascript
Vue项目结合Vue-layer实现弹框式编辑功能(实例代码)
2020/03/11 Javascript
Vue实现PC端靠边悬浮球的代码
2020/05/09 Javascript
Vue-cli打包后如何本地查看的操作
2020/09/02 Javascript
[01:55]2014DOTA2国际邀请赛快报:国土生病 紧急去医院治疗
2014/07/10 DOTA
python动态加载变量示例分享
2014/02/17 Python
python通过pil将图片转换成黑白效果的方法
2015/03/16 Python
Python利用递归和walk()遍历目录文件的方法示例
2017/07/14 Python
Python爬虫实例爬取网站搞笑段子
2017/11/08 Python
详解Python连接MySQL数据库的多种方式
2019/04/16 Python
python selenium登录豆瓣网过程解析
2019/08/10 Python
PyTorch中Tensor的数据类型和运算的使用
2020/09/03 Python
Abbacino官网:包、钱包和女士配饰
2019/04/15 全球购物
Linux文件操作命令都有哪些
2016/07/23 面试题
大学生毕业求职找工作的自我评价
2013/09/29 职场文书
计算机专业自我鉴定
2013/10/15 职场文书
前台文员的岗位职责
2013/11/14 职场文书
给酒店员工的表扬信
2014/01/11 职场文书
餐厅经理岗位职责范本
2014/02/17 职场文书
小学生环保标语
2014/06/13 职场文书
通知的写法
2015/04/23 职场文书
开天辟地观后感
2015/06/09 职场文书
python opencv常用图形绘制方法(线段、矩形、圆形、椭圆、文本)
2021/04/12 Python
教你怎么用Python处理excel实现自动化办公
2021/04/30 Python