Home  >  Article  >  Backend Development  >  Python's Tornado framework implements asynchronous non-blocking access to the database

Python's Tornado framework implements asynchronous non-blocking access to the database

高洛峰
高洛峰Original
2017-03-01 13:45:072405browse

tornado is an http non-blocking server. To use it, we will use the tornado framework, mongodb database and motor (mongodb's asynchronous driver) to simply implement the non-blocking function of tornado.

Download and installation supported by other environments

1. Install mongodb

$ sudo apt-get install update
$ sudo apt-get install mongodb

2. Install motor

$ pip install motor

non-blocking

# 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

First connect to the database in the configuration file, db_name in client.db_name is the name of the database

 # handlers/__init__.py
class BaseHandler(tornado.web.RequestHandler, TemplateRendering):
  def initialite(self):
    ...

  @property
  def db(self):
    return self.application.db

Add db() and use property Decoration, access the database like a 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 decoration makes the function non-blocking, returning a generator instead of using a callback function. motor Asynchronous is also implemented through yield (otherwise a callback function must be returned). In fact, this example does not reflect the blocking problem. The key is that the time is too short.
Let’s modify the code

# 之前
yield self.db.user.insert(data)

# 之后
yield tornado.gen.Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + 10)

The application is blocked here through tornado.ioloop.IOLoop.instance().add_timeout, which is a non-blocking implementation of time.sleep. If time.sleep is used here, because tornado is a single thread, it will block the entire application, so other handlers It is also inaccessible.
You can see that after registering on the registration page, I clicked /auth/login during the blocking period and directly accessed the login page to complete the non-blocking process.

Redirect problem under asynchronous
I often encounter some problems when using tornado, so I will write down the problems encountered and the solutions (I would like to thank the pythonistas who helped me answer my doubts)

1. Question

I want to implement a registered user function. The web framework uses tornado database and mongodb but an Exception redirect error occurs during registration. Now paste the code:

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('/')

I originally wanted to jump to the registration page if the user enters the verification code incorrectly, but the problem is that if the verification code is incorrect, the code will continue to be executed. Although self is added after self.redirect. finish will terminate the code, but because there is already self.finish in the self.redirect function, there are two abnormal termination codes.
Because of the above reasons, the code will not be terminated, and users will still register if the verification code is wrong.

2.Solution

return self.redirect('/auth/register')

or

self.redirect('/auth/register')
return

(1) The answer given by the enthusiastic user rsj217 in segmentdefault
self.finish will turn off the request, because @tornado.web.aynchronous tells tornado that it will wait for the request (long link). self. Redirect is equivalent to setting the location attribute of the response headers.

(2) The answer given by the enthusiastic user Evian in segmentdefault
self.finish will of course not jump out of the function, otherwise you will want to do something after the request is completed. What to do.

3. Summary

The above problem occurs because self.finish is mistakenly regarded as a jump-out function

self.redirect will Set location in request.headers for jump

self.finish will close the request, but will not jump out of the function

More Python's Tornado framework implements asynchronous non-blocking access to the database related articles Please pay attention to PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn