Python的Tornado框架实现异步非阻塞访问数据库的示例

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会关掉请求, 但不会跳出函数
(0)

相关推荐

  • 科学知识:同步、异步、阻塞和非阻塞区别

    简单点说: 阻塞就是干不完不准回来,一直处于等待中,直到事情处理完成才返回: 非阻塞就是你先干,我先看看有其他事没有,一发现事情被卡住,马上报告领导. 我们拿最常用的send和recv两个函数来说吧... 比如你调用send函数发送一定的Byte,在系统内部send做的工作其实只是把数据传输(Copy)到TCP/IP协议栈的输出缓冲区,它执行成功并不代表数据已经成功的发送出去了,如果TCP/IP协议栈没有足够的可用缓冲区来保存你Copy过来的数据的话...这时候就体现出阻塞和非阻塞的不同之处了:

  • 详解socket阻塞与非阻塞,同步与异步、I/O模型

    socket阻塞与非阻塞,同步与异步 1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步/异步主要针对C端: 同步: 所谓同步,就是在c端发出一个功能调用时,在没有得到结果之前,该调用就不返回.也就是必须一件一件事做,等前一件做完了才能做下一件事. 例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事 异步: 异步的概念和同步相对.当c端一个异步

  • 浅谈socket同步和异步、阻塞和非阻塞、I/O模型

    在进行网络编程时,常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式 同步/异步主要针对C端: 同步:c端发出一个功能调用时,在没有得到结果之前,c端死等结果 例如:普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事 异步:c端一个异步过程调用发出后,调用者不会立刻得到结果.实际处理这个调用的部件在完成后,通过状态.通知和回调来通知调用者. 例如:ajax请求(异步): 事件触发->服务

  • 200行自定义python异步非阻塞Web框架

    Python的Web框架中Tornado以异步非阻塞而闻名.本篇将使用200行代码完成一个微型异步非阻塞Web框架:Snow. 一.源码 本文基于非阻塞的Socket以及IO多路复用从而实现异步非阻塞的Web框架,其中便是众多异步非阻塞Web框架内部原理. #!/usr/bin/env python # -*- coding:utf-8 -*- import re import socket import select import time class HttpResponse(object)

  • java 同步、异步、阻塞和非阻塞分析

    java 同步.异步.阻塞和非阻塞分析 概要: 正常情况下,我们的程序以同步非阻塞的方式在运行.但是我们的程序总会出现一些耗时操作,比如复杂的计算(找出1到10亿之间的素数)和程序本身无法控制的操作(IO操作.网络请求).包含这些耗时操作的方法我们可以把它称为阻塞方法,包含这些耗时操作的任务我们可以把它称为阻塞任务.阻塞与非阻塞是以是否耗时来定义的. 如果程序中存在大量阻塞操作,就会影响程序性能.但是阻塞的存在是客观事实,我们的程序是无法改变它的,一个网络请求需要3秒才能响应,我们不可能让它1毫

  • Python的Tornado框架实现异步非阻塞访问数据库的示例

    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 moto

  • Python的Tornado框架的异步任务与AsyncHTTPClient

    高性能服务器Tornado Python的web框架名目繁多,各有千秋.正如光荣属于希腊,伟大属于罗马.Python的优雅结合WSGI的设计,让web框架接口实现千秋一统.WSGI 把应用(Application)和服务器(Server)结合起来.Django 和 Flask 都可以结合 gunicon 搭建部署应用. 与 django 和 flask 不一样,tornado 既可以是 wsgi 应用,也可以是 wsgi 服务.当然,选择tornado更多的考量源于其单进程单线程异步IO的网络模

  • Python的Tornado框架异步编程入门实例

    Tornado Tornado 是一款非阻塞可扩展的使用Python编写的web服务器和Python Web框架, 可以使用Tornado编写Web程序并不依赖任何web服务器直接提供高效的web服务.所以Tornado不仅仅是一个web框架而且还是一款可以用于生产环境的高效的web服务器 Torando 在Linux和FreeBSD上使用高效的异步I/O模型 epoll 和kqueue来实现高效的web服务器, 所以 tornado在Linux上和FreeBSD系列性能可以达到最高 接口 当然

  • Flask实现异步非阻塞请求功能实例解析

    本文研究的主要是Flask实现异步非阻塞请求功能,具体实现如下. 最近做物联网项目的时候需要搭建一个异步非阻塞的HTTP服务器,经过查找资料,发现可以使用gevent包. 关于gevent Gevent 是一个 Python 并发网络库,它使用了基于 libevent 事件循环的 greenlet 来提供一个高级同步 API.下面是代码示例: from gevent.wsgi import WSGIServer from yourapplication import app http_serve

  • 使用Python的Twisted框架编写非阻塞程序的代码示例

    先来看一段代码: # ~*~ Twisted - A Python tale ~*~ from time import sleep # Hello, I'm a developer and I mainly setup Wordpress. def install_wordpress(customer): # Our hosting company Threads Ltd. is bad. I start installation and... print "Start installation

  • 使用Python的Tornado框架实现一个简单的WebQQ机器人

    我打算将WebQQ单独出来运行, 一开始直接拷贝了pyxmpp2的mainloop, 但是跑起来问题多多, 所以我又研究了利用Tornado进行网络编程(这里), 所以我放弃了Pyxmpp2的mainloop,使用Tornado进行重写 首先放出项目代码 引子 WebQQ协议是一套基于HTTP的QQ协议, 而用Python的urllib2库进行请求太慢, 因为HTTP本身就使用socket请求, 所以改用多路复用I/O模型, 而Tornado简单高效, 看过代码后可以轻松上手.平台兼容性很好,

  • Java异步非阻塞编程的几种方式总结

    1 服务端执行,最简单的同步调用方式: 缺陷: 服务端响应之前,IO会阻塞在: java.net.SocketInputStream#socketRead0 的native方法上: 2 JDK NIO & Future java 1.5之后 优点:主线程可以不用等待IO响应,可以去做点其他的,比如说再发送一个IO请求,可以等到一起返回; 缺点:主线程在等待结果返回过程中依然需要等待,没有根本解决此问题; 3 使用Callback回调方式 优点:主线程完成发送请求后,再也不用关心这个逻辑,去执行其

  • Python中使用select模块实现非阻塞的IO

    Socket的英文原义是"孔"或"插座".作为BSD UNIX的进程通信机制,取后一种意思.通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.在Internet上的主机一般运行了多个服务软件,同时提供几种服务.每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务.Socket正如其英文原意那样,像一个多孔插座.一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110

  • ThinkPHP 框架实现的读取excel导入数据库操作示例

    本文实例讲述了ThinkPHP 框架实现的读取excel导入数据库操作.分享给大家供大家参考,具体如下: 入口文件中: require_once VENDOR_PATH.'PHPExcel/PHPExcel/IOFactory.php'; require_once VENDOR_PATH.'PHPExcel/PHPExcel.php'; PHP: namespace Home\Controller; class ExcelController extends CommonController {

随机推荐