brief introduction

Tornado  yes  FriendFeed  The extensible non blocking web Open source versions of servers and related tools . This Web The frame looks a bit like web.py  perhaps  Google Of webapp, However, in order to effectively utilize the non blocking server environment , This Web The framework also contains some useful tools And optimization .

Tornado And now the mainstream Web Server framework ( Including most Python Framework ) There is a clear difference : It's a non blocking server , And it's pretty fast . Benefit from Non blocking way and right epoll The use of ,Tornado Thousands of connections can be processed per second , This means for real time Web For service ,Tornado It's an ideal Web frame . We developed this Web The main purpose of the server is to deal with FriendFeed In real time —— stay FriendFeed Every active user in the application will maintain a server connection .

Tornado install

pip3 install tornado

First time to know Tornado

Start by opening pycharm, Build a clean one project

Create a new one py file :

import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler): # Be similar to Django Inside CBV
def get(self): # get Method
self.write("Hello, world") application = tornado.web.Application([ # Be similar to Django The routing system inside
(r"/index", MainHandler),
]) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start() # start-up

app.py

Run the file and open the browser to access the web address :

Run the script , Execute sequentially :

  • Create a Application object , And put a regular expression '/' And class names MainHandler Pass in the constructor :tornado.web.Application(...)
  • perform Application Object's listen(...) Method , namely :application.listen(8888)
  • perform IOLoop Of class of class of start() Method , namely :tornado.ioloop.IOLoop.instance().start()

The whole process is actually creating a socket Server and monitor 8888 port , When the request comes , According to the url And how to request (post、get or put etc. ) To specify the method in the corresponding class to handle this request , In the above demo Chinese only url by http://127.0.0.1:8888/index The request specified the processing class MainHandler( See below for details ). therefore , Access on Browser :http://127.0.0.1:8888/index, The server will return to the browser Hello,world , Otherwise return to 404: Not Found(tornado Internally defined values ),  That is to finish once http Requests and responses .

From the above analysis , We will take the whole Web The framework is divided into two parts :

  • Waiting for the request phase , namely : Create server socket And monitor the port
  • Processing the request phase , namely : When there is a client connection , Accept the request , And according to the different request to make the corresponding corresponding

classical Login Case study

First , Create template folder and template file :

login.html file

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="POST" action="/login">
<input type="text" name="username" />
<input type="submit" value=" Submit " />
</form>
</body>
</html>

Create a new route :

 (r"/login", LoginHandler),

newly build CBV:

class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.render("login.html") def post(self, *args, **kwargs):
# obtain url China and Israel GET Data transferred by form
self.get_query_argument()
self.get_query_arguments()
# Go to the request body to get the passed data
self.get_body_argument()
self.get_body_arguments()
# Go to get&post Both sides get the data
user = self.get_argument('username')
print(user)
self.redirect('www.baidu.com')

Register template file path :

# Affirming settings, Dictionary format 
settings = {
'template_path': 'tmp'
} # Sign up for tornado Inside
application = tornado.web.Application([
(r"/index", MainHandler),
(r"/login", LoginHandler),
], **settings)

whole app file :

#!/usr/bin/env python3
# encoding: utf-8
# Author: Dandy import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world") class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.render("login.html") def post(self, *args, **kwargs):
# obtain url China and Israel GET Data transferred by form
self.get_query_argument()
self.get_query_arguments()
# Go to the request body to get the passed data
self.get_body_argument()
self.get_body_arguments()
# Go to get&post Both sides get the data
user = self.get_argument('username')
print(user)
self.redirect('www.baidu.com') settings = {
'template_path': 'tmp'
} application = tornado.web.Application([
(r"/index", MainHandler),
(r"/login", LoginHandler),
], **settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

At this point, it can be executed

About static files

First add a static folder , And put in a picture

Static path registration :

settings = {
'template_path': 'tmp',
'static_path': 'statics'
}

login Add pictures to the page 、 Note that the following path says static, Standard usage

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="POST" action="/login">
<input type="text" name="username" />
<input type="submit" value=" Submit " />
</form>
<img src="/static/ Elegant .jpeg" />
</body>
</html>

Now visit the page

Can we not static As a static folder name ?

The answer, of course, is yes , Just register like this :

settings = {
'template_path': 'tmp',
'static_path': 'statics',
'static_url_prefix': '/jingtai/', # Here is the user-defined static path name
}

Go ahead and revise login Just the one on the web .

Detailed introduction

This is a brief introduction to totornado Some of the basic use of building . Now spend some time to solve the basic problems of each module in detail .

One 、 Routing system

The routing system starts from the front Django It's not difficult to sum up in the process of learning , In fact, that is url The corresponding relationship with the view function . Of course! , stay Django Inside , Yes FBV and CBV The concept of ; And in the tornado Every url The corresponding function is a class , namely CBV.

import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world") class StoryHandler(tornado.web.RequestHandler):
def get(self, story_id):
self.write("You requested the story " + story_id) class HomeHandler(tornado.web.RequestHandler):
def get(self):
self.write("dandy") application = tornado.web.Application([
(r"/index", MainHandler),
(r"/story/([0-9]+)", StoryHandler),
]) application.add_handlers(r"^a\.com$", [
(r"/", HomeHandler), # route a.com:8888/
]) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

Tornado Zhongyuan supports the routing of secondary domain names , Such as :

Two 、 template engine

Tornao Template language and django It's similar to , The template engine loads the template file into memory , And then embed the data in it , Finally get a complete string , Return the string to the requester .

Tornado Template support for “ Control statement ” and “ Express a statement ”, The control statement is to use  {%  and  %}  Wrapped up for example  {% if len(items) > 2 %}. The expression statement is to use  {{  and  }}  Wrapped up , for example  {{ items[0] }}.

Control statements and corresponding Python The format of the statement is basically the same . We support  ifforwhile  and  try, The logical end of these statements needs to use  {% end %}  Make a mark . And through  extends  and  block  Statement implements template inheritance .

notes : Before using a template, you need to setting Set the template path in :"template_path" : "tpl"

1、 Basic use

import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html", list_info=[11, 22, 33], key1='value1') settings = {
'template_path': 'tmp'
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

app.py

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href='{{static_url("css/common.css")}}' rel="stylesheet" />
</head>
<body>
<h1>{{ key1 }}</h1>
<ul>
{% for item in list_info %}
{% if item > 12 %}
<li>{{ item }}</li>
{% end %}
{% end %}
</ul>
</body>
</html>

index.html

Be similar to static_url Other methods

 Some functions are provided by default in the template 、 Field 、 Class for template use :
escape: tornado.escape.xhtml_escape Another name for 
xhtml_escape: tornado.escape.xhtml_escape Another name for
url_escape: tornado.escape.url_escape Another name for
json_encode: tornado.escape.json_encode Another name for
squeeze: tornado.escape.squeeze Another name for
linkify: tornado.escape.linkify Another name for
datetime: Python Of datetime module
handler: Current RequestHandler object
request: handler.request Another name for
current_user: handler.current_user Another name for
locale: handler.locale Another name for
_: handler.locale.translate Another name for
static_url: for handler.static_url Another name for
xsrf_form_html: handler.xsrf_form_html Another name for

2、 Master

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>title</title>
<link href='{{static_url("css/common.css")}}' rel="stylesheet" />
{% block CSS %}{% end %}
</head>
<body> <div class="pg-header"> </div> {% block RenderBody %}{% end %} <script src='{{static_url("js/jquery-1.8.2.min.js")}}'></script> {% block JavaScript %}{% end %}
</body>
</html> layout.html
{% extends 'layout.html'%}
{% block CSS %}
<link href='{{static_url("css/index.css")}}' rel="stylesheet" />
{% end %} {% block RenderBody %}
<h1>Index</h1> <ul>
{% for item in li %}
<li>{{item}}</li>
{% end %}
</ul> {% end %} {% block JavaScript %} {% end %}

Inherit page

3、 Import small tags

<div>
<ul>
<li>dandy</li>
<li>durant</li>
</ul>
</div>

tag

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title></title>
</head>
<body> <div class="pg-header">
{% include 'header.html' %}
</div> </body>
</html>

index.html

4、 Template function extension UIMethod&UIModule

a、 Affirming

def tab(self):
return 'UIMethod'

uimethods.py

from tornado.web import UIModule
from tornado import escape class custom(UIModule): def render(self, *args, **kwargs):
return escape.xhtml_escape('<h1>wuzdandz</h1>')

uimodules.py

b、 register

import uimodules as md
import uimethods as mt
settings = {
'template_path': 'template',
'static_path': 'static',
'ui_methods': mt,
'ui_modules': md,
}

app.py

c、 Use

<body>
{% module custom(123) %}
<br>
{{ tab() }}
</body>

html Text

Pay attention to these methods :

embedded_css/javascript Used to generate css or js Of the statement

javascript/css_files Used to generate link Point to file

3、 ... and 、 Static files

For static files , You can configure the directory of static files and the prefix used in the previous paragraph , also Tornaodo It also supports static file caching .

settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
}
application = tornado.web.Application([
(r"/index", MainHandler),
], **settings)
<img src="/static/ Elegant .jpeg" />
<link href='{{static_url("commons.css")}}' rel="stylesheet" />

html

def get_content_version(cls, abspath):
"""Returns a version string for the resource at the given path. This class method may be overridden by subclasses. The
default implementation is a hash of the file's contents. .. versionadded:: 3.1
"""
data = cls.get_content(abspath)
hasher = hashlib.md5()
if isinstance(data, bytes):
hasher.update(data)
else:
for chunk in data:
hasher.update(chunk)
return hasher.hexdigest()

Implement static file caching

Four 、cookie

Tornado You can be right cookie To operate , And it can be used to cookie Sign to place forgery .

1、 Basic operation

class MainHandler(tornado.web.RequestHandler):
def get(self):
if not self.get_cookie("username"): # obtain cookie
self.set_cookie("username", "dandy") # Set up cookie
self.write("Your cookie was not set yet!")
else:
self.write("Your cookie was set!")

2、 encryption cookie( Signature )

Cookie It's easy to be forged by malicious clients . You want to join in cookie Save the current login user's id Information like that , You need to cookie Sign to prevent forgery .Tornado adopt set_secure_cookie and get_secure_cookie Method directly supports this function . Use these methods , You need to provide a key when you create the app , The name is cookie_secret. You can pass it as a keyword parameter into the application settings :

class MainHandler(tornado.web.RequestHandler):
def get(self):
if not self.get_secure_cookie("mycookie"):
self.set_secure_cookie("mycookie", "myvalue")
self.write("Your cookie was not set yet!")
else:
self.write("Your cookie was set!") application = tornado.web.Application([
(r"/", MainHandler),
], cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")
def _create_signature_v1(secret, *parts):
hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
for part in parts:
hash.update(utf8(part))
return utf8(hash.hexdigest()) # encryption
def _create_signature_v2(secret, s):
hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)
hash.update(utf8(s))
return utf8(hash.hexdigest()) def create_signed_value(secret, name, value, version=None, clock=None,
key_version=None):
if version is None:
version = DEFAULT_SIGNED_VALUE_VERSION
if clock is None:
clock = time.time timestamp = utf8(str(int(clock())))
value = base64.b64encode(utf8(value))
if version == 1:
signature = _create_signature_v1(secret, name, value, timestamp)
value = b"|".join([value, timestamp, signature])
return value
elif version == 2:
# The v2 format consists of a version number and a series of
# length-prefixed fields "%d:%s", the last of which is a
# signature, all separated by pipes. All numbers are in
# decimal format with no leading zeros. The signature is an
# HMAC-SHA256 of the whole string up to that point, including
# the final pipe.
#
# The fields are:
# - format version (i.e. 2; no length prefix)
# - key version (integer, default is 0)
# - timestamp (integer seconds since epoch)
# - name (not encoded; assumed to be ~alphanumeric)
# - value (base64-encoded)
# - signature (hex-encoded; no length prefix)
def format_field(s):
return utf8("%d:" % len(s)) + utf8(s)
to_sign = b"|".join([
b"",
format_field(str(key_version or 0)),
format_field(timestamp),
format_field(name),
format_field(value),
b'']) if isinstance(secret, dict):
assert key_version is not None, 'Key version must be set when sign key dict is used'
assert version >= 2, 'Version must be at least 2 for key version support'
secret = secret[key_version] signature = _create_signature_v2(secret, to_sign)
return to_sign + signature
else:
raise ValueError("Unsupported version %d" % version) # Decrypt
def _decode_signed_value_v1(secret, name, value, max_age_days, clock):
parts = utf8(value).split(b"|")
if len(parts) != 3:
return None
signature = _create_signature_v1(secret, name, parts[0], parts[1])
if not _time_independent_equals(parts[2], signature):
gen_log.warning("Invalid cookie signature %r", value)
return None
timestamp = int(parts[1])
if timestamp < clock() - max_age_days * 86400:
gen_log.warning("Expired cookie %r", value)
return None
if timestamp > clock() + 31 * 86400:
# _cookie_signature does not hash a delimiter between the
# parts of the cookie, so an attacker could transfer trailing
# digits from the payload to the timestamp without altering the
# signature. For backwards compatibility, sanity-check timestamp
# here instead of modifying _cookie_signature.
gen_log.warning("Cookie timestamp in future; possible tampering %r",
value)
return None
if parts[1].startswith(b""):
gen_log.warning("Tampered cookie %r", value)
return None
try:
return base64.b64decode(parts[0])
except Exception:
return None def _decode_fields_v2(value):
def _consume_field(s):
length, _, rest = s.partition(b':')
n = int(length)
field_value = rest[:n]
# In python 3, indexing bytes returns small integers; we must
# use a slice to get a byte string as in python 2.
if rest[n:n + 1] != b'|':
raise ValueError("malformed v2 signed value field")
rest = rest[n + 1:]
return field_value, rest rest = value[2:] # remove version number
key_version, rest = _consume_field(rest)
timestamp, rest = _consume_field(rest)
name_field, rest = _consume_field(rest)
value_field, passed_sig = _consume_field(rest)
return int(key_version), timestamp, name_field, value_field, passed_sig def _decode_signed_value_v2(secret, name, value, max_age_days, clock):
try:
key_version, timestamp, name_field, value_field, passed_sig = _decode_fields_v2(value)
except ValueError:
return None
signed_string = value[:-len(passed_sig)] if isinstance(secret, dict):
try:
secret = secret[key_version]
except KeyError:
return None expected_sig = _create_signature_v2(secret, signed_string)
if not _time_independent_equals(passed_sig, expected_sig):
return None
if name_field != utf8(name):
return None
timestamp = int(timestamp)
if timestamp < clock() - max_age_days * 86400:
# The signature has expired.
return None
try:
return base64.b64decode(value_field)
except Exception:
return None def get_signature_key_version(value):
value = utf8(value)
version = _get_version(value)
if version < 2:
return None
try:
key_version, _, _, _, _ = _decode_fields_v2(value)
except ValueError:
return None return key_version

Internal algorithm

Signature Cookie The essence is :

 Write cookie The process :
Set the value to base64 encryption 
Sign anything other than values , The hash algorithm ( Unable to reverse parse )
Splicing Signature + Encrypted value
read cookie The process : Read Signature + Encrypted value
Verify signature
base64 Decrypt , Get value content

notes : many API Authentication mechanism and security cookie The implementation mechanism is the same .

#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self):
login_user = self.get_secure_cookie("login_user", None)
if login_user:
self.write(login_user)
else:
self.redirect('/login') class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.current_user() self.render('login.html', **{'status': ''}) def post(self, *args, **kwargs): username = self.get_argument('name')
password = self.get_argument('pwd')
if username == 'wupeiqi' and password == '':
self.set_secure_cookie('login_user', ' Wu Peiqi ')
self.redirect('/')
else:
self.render('login.html', **{'status': ' Wrong user name or password '}) settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh'
} application = tornado.web.Application([
(r"/index", MainHandler),
(r"/login", LoginHandler),
], **settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

be based on Cookie Implement user authentication -Demo

#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web class BaseHandler(tornado.web.RequestHandler): def get_current_user(self):
return self.get_secure_cookie("login_user") class MainHandler(BaseHandler): @tornado.web.authenticated
def get(self):
login_user = self.current_user
self.write(login_user) class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.current_user() self.render('login.html', **{'status': ''}) def post(self, *args, **kwargs): username = self.get_argument('name')
password = self.get_argument('pwd')
if username == 'wupeiqi' and password == '':
self.set_secure_cookie('login_user', ' Wu Peiqi ')
self.redirect('/')
else:
self.render('login.html', **{'status': ' Wrong user name or password '}) settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
'login_url': '/login'
} application = tornado.web.Application([
(r"/index", MainHandler),
(r"/login", LoginHandler),
], **settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

Based on signature Cookie Implement user authentication -Demo

3、javascript operation cookie

because Cookie Save in browser side , So it can also be used on the browser side JavaScript To operate Cookie.

/*
Set up cookie, Specify the number of seconds to expire
*/
function setCookie(name,value,expires){
var temp = [];
var current_date = new Date();
current_date.setSeconds(current_date.getSeconds() + 5);
document.cookie = name + "= "+ value +";expires=" + current_date.toUTCString();
}

Of course, it is more recommended to use jquery cookie To operate cookie

5、 ... and 、CSRF

Tornado Cross Site Request Forgery and Django The similarity in

1、 Enable the Settings

settings = {
"xsrf_cookies": True,
}
application = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
], **settings)

setting start-up xsrf

2、 Forms

<form action="/new_message" method="post">
{% raw xsrf_form_html() %}
<input type="text" name="message"/>
<input type="submit" value="Post"/>
</form>

form

3、ajax

function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
} jQuery.postJSON = function(url, args, callback) {
args._xsrf = getCookie("_xsrf");
$.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
success: function(response) {
callback(eval("(" + response + ")"));
}});
};

ajax

notes :Ajax When using , In essence, it's about getting local knowledge cookie, carry cookie Then send the request

6、 ... and 、 Upload files

1、form Form upload

<body>
<form name="form" action="/index" method="POST" enctype="multipart/form-data" >
<input name="fafafa" id="my_file" type="file" />
<input type="submit" value=" Submit " />
</form>
</body>

form

import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self): self.render('index.html') def post(self, *args, **kwargs):
file_metas = self.request.files["fafafa"]
# print(file_metas)
for meta in file_metas:
file_name = meta['filename']
with open(file_name,'wb') as up:
up.write(meta['body']) settings = {
'template_path': 'template',
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()

py

2、ajax Upload

<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" />
<script>
function UploadFile(){
var fileObj = document.getElementById("img").files[0]; var form = new FormData();
form.append("k1", "v1");
form.append("fff", fileObj); var xhr = new XMLHttpRequest();
xhr.open("post", '/index', true);
xhr.send(form);
}
</script>
</body>

html - xmlhttprequest

<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" />
<script>
function UploadFile(){
var fileObj = $("#img")[0].files[0];
var form = new FormData();
form.append("k1", "v1");
form.append("fff", fileObj); $.ajax({
type:'POST',
url: '/index',
data: form,
processData: false, // tell jQuery not to process the data
contentType: false, // tell jQuery not to set contentType
success: function(arg){
console.log(arg);
}
})
}
</script>
</body>

html - jquery

<body>
<form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" >
<div id="main">
<input name="fff" id="my_file" type="file" />
<input type="button" name="action" value="Upload" onclick="redirect()"/>
<iframe id='my_iframe' name='my_iframe' src="" class="hide"></iframe>
</div>
</form> <script>
function redirect(){
document.getElementById('my_iframe').onload = Testt;
document.getElementById('my_form').target = 'my_iframe';
document.getElementById('my_form').submit(); } function Testt(ths){
var t = $("#my_iframe").contents().find("body").text();
console.log(t);
}
</script>
</body>

html - iframe

import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self): self.render('index.html') def post(self, *args, **kwargs):
file_metas = self.request.files["fff"]
# print(file_metas)
for meta in file_metas:
file_name = meta['filename']
with open(file_name,'wb') as up:
up.write(meta['body']) settings = {
'template_path': 'template',
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()

py

<script type="text/javascript">
$(document).ready(function () {
$("#formsubmit").click(function () {
var iframe = $('<iframe name="postiframe" id="postiframe" style="display: none"></iframe>');
$("body").append(iframe);
var form = $('#theuploadform');
form.attr("action", "/upload.aspx");
form.attr("method", "post"); form.attr("encoding", "multipart/form-data");
form.attr("enctype", "multipart/form-data"); form.attr("target", "postiframe");
form.attr("file", $('#userfile').val());
form.submit(); $("#postiframe").load(function () {
iframeContents = this.contentWindow.document.body.innerHTML;
$("#textarea").html(iframeContents);
}); return false; }); }); </script> <form id="theuploadform">
<input id="userfile" name="userfile" size="" type="file" />
<input id="formsubmit" type="submit" value="Send File" />
</form> <div id="textarea">
</div>

be based on iframe Realization Ajax Upload sample

$('#upload_iframe').load(function(){
var iframeContents = this.contentWindow.document.body.innerText;
iframeContents = JSON.parse(iframeContents); })
function bindChangeAvatar1() {
$('#avatarImg').change(function () {
var file_obj = $(this)[0].files[0];
$('#prevViewImg')[0].src = window.URL.createObjectURL(file_obj)
})
} function bindChangeAvatar2() {
$('#avatarImg').change(function () {
var file_obj = $(this)[0].files[0];
var reader = new FileReader();
reader.readAsDataURL(file_obj);
reader.onload = function (e) {
$('#previewImg')[0].src = this.result;
};
})
} function bindChangeAvatar3() {
$('#avatarImg').change(function () {
var file_obj = $(this)[0].files[0];
var form = new FormData();
form.add('img_upload', file_obj); $.ajax({
url: '',
data: form,
processData: false, // tell jQuery not to process the data
contentType: false, // tell jQuery not to set contentType
success: function (arg) { }
})
})
} function bindChangeAvatar4() {
$('#avatarImg').change(function () {
$(this).parent().submit(); $('#upload_iframe').load(function () {
var iframeContents = this.contentWindow.document.body.innerText;
iframeContents = JSON.parse(iframeContents);
if (iframeContents.status) {
$('#previewImg').attr('src', '/' + iframeContents.data);
}
}) })
} other

....

7、 ... and 、 Asynchronous non-blocking

1、 Basic use

First , Let's start with a piece of code

class AsyncHandler(tornado.web.RequestHandler):
def get(self):
import time
time.sleep(20)
self.write('') application = tornado.web.Application([
(r"/index", MainHandler),
(r"/async", AsyncHandler),
], **settings)

Understanding asynchronous nonblocking , The first point must be , Single thread ; So test the code above , It's not hard to find out : To open the first async page , There was no return , Always show loading , Now open index, Find out index At the same time, the page is loading out .

When async After execution ,index The page soon returned .

Modify the code at this time :

#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web
from tornado.web import UIModule
from tornado import gen
from tornado.concurrent import Future
import time class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html", list_info=[11, 22, 33], key1='value1') class AsyncHandler(tornado.web.RequestHandler): @gen.coroutine
def get(self):
future = Future()
tornado.ioloop.IOLoop.current().add_timeout(time.time() + 20, self.doing)
yield future def doing(self, *args, **kwargs):
self.write('async')
self.finish() settings = {
'template_path': 'tmp',
'static_path': 'statics'
} application = tornado.web.Application([
(r"/index", MainHandler),
(r"/async", AsyncHandler),
], **settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

Then perform the above operation , You'll find that when async Blocking waiting for return , Other web pages are accessible . In this way, more requests are processed per unit time . On the server side, use single thread to the extreme .

Decorator + Future So as to achieve Tornado Asynchronous non blocking , This is the core of asynchronous non blocking Future On , This future Class is a bit like a tag , When decorators decorate functions , Function internal instantiation Future, And add a callback function . Execute code :

from tornado import gen
from tornado.concurrent import Future class AsyncHandler(tornado.web.RequestHandler): @gen.coroutine
def get(self):
future = Future()
future.add_done_callback(self.doing)
yield future
# or
# tornado.ioloop.IOLoop.current().add_future(future,self.doing)
# yield future def doing(self, *args, **kwargs):
self.write('async')
self.finish()

Found that the request never returned . In fact, instantiation future after , There is a flag, Only when the response data is ready or the response conditions are met can the callback be made callback function , Return to the client .

2、 Synchronous blocking versus asynchronous nonblocking

The first example above is actually an example of synchronization blocking .

class SyncHandler(tornado.web.RequestHandler):
def get(self):
self.doing()
self.write('sync') def doing(self):
time.sleep(10)

Synchronous blocking

class AsyncHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
future = Future()
tornado.ioloop.IOLoop.current().add_timeout(time.time() + 5, self.doing)
yield future def doing(self, *args, **kwargs):
self.write('async')
self.finish()

Asynchronous non-blocking

3、httpclient Class library

Tornado Provides httpclient Class libraries are used to send Http request , Can be used with asynchronous non blocking .

import tornado.web
from tornado import gen
from tornado import httpclient # Mode one :
class AsyncHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self, *args, **kwargs):
print(' Get into ')
http = httpclient.AsyncHTTPClient()
data = yield http.fetch("http://www.google.com")
print('K.O.',data)
self.finish('') # Mode two :
# class AsyncHandler(tornado.web.RequestHandler):
# @gen.coroutine
# def get(self):
# print(' Get into ')
# http = httpclient.AsyncHTTPClient()
# yield http.fetch("http://www.google.com", self.done)
#
# def done(self, response):
# print('K.O.')
# self.finish('666') application = tornado.web.Application([
(r"/async", AsyncHandler),
]) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

Speaking of this , Let's test another one url To change future Tag bit of .

Add :

import tornado.ioloop
import tornado.web
from tornado.web import UIModule
from tornado import gen
from tornado.concurrent import Future future = None class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html", list_info=[11, 22, 33], key1='value1') class AsyncHandler(tornado.web.RequestHandler): @gen.coroutine
def get(self):
global future
future = Future() future.add_done_callback(self.doing)
yield future
# or
# tornado.ioloop.IOLoop.current().add_future(future,self.doing)
# yield future def doing(self, *args, **kwargs):
self.write('async')
self.finish() class StopHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
future.set_result('...') settings = {
'template_path': 'tmp',
'static_path': 'statics'
} application = tornado.web.Application([
(r"/index", MainHandler),
(r"/async", AsyncHandler),
(r"/stop", StopHandler),
], **settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

Distal url Control someone url Of future

200 Custom asynchronous non blocking of rows web frame

#!/usr/bin/env python3
# encoding: utf-8
# Author: Dandy
import re
import socket
import select
import time class HttpResponse(object):
"""
Encapsulating response information
"""
def __init__(self, content=''):
self.content = content self.headers = {}
self.cookies = {} def response(self):
return bytes(self.content, encoding='utf-8') class HttpNotFound(HttpResponse):
"""
404 When the error prompt
"""
def __init__(self):
super(HttpNotFound, self).__init__('404 Not Found') class HttpRequest(object):
"""
User encapsulates user request information
"""
def __init__(self, conn):
self.conn = conn self.header_bytes = bytes()
self.header_dict = {}
self.body_bytes = bytes() self.method = ""
self.url = ""
self.protocol = "" self.initialize()
self.initialize_headers() def initialize(self): header_flag = False
while True:
try:
received = self.conn.recv(8096)
except Exception as e:
received = None
if not received:
break
if header_flag:
self.body_bytes += received
continue
temp = received.split(b'\r\n\r\n', 1)
if len(temp) == 1:
self.header_bytes += temp
else:
h, b = temp
self.header_bytes += h
self.body_bytes += b
header_flag = True @property
def header_str(self):
return str(self.header_bytes, encoding='utf-8') def initialize_headers(self):
headers = self.header_str.split('\r\n')
first_line = headers[0].split(' ')
if len(first_line) == 3:
self.method, self.url, self.protocol = headers[0].split(' ')
for line in headers:
kv = line.split(':')
if len(kv) == 2:
k, v = kv
self.header_dict[k] = v class Future(object):
"""
Encapsulation of callback functions and readiness in asynchronous non blocking mode
"""
def __init__(self, callback):
self.callback = callback
self._ready = False
self.value = None def set_result(self, value=None):
self.value = value
self._ready = True @property
def ready(self):
return self._ready class TimeoutFuture(Future):
"""
Asynchronous non blocking timeout
"""
def __init__(self, timeout):
super(TimeoutFuture, self).__init__(callback=None)
self.timeout = timeout
self.start_time = time.time() @property
def ready(self):
current_time = time.time()
if current_time > self.start_time + self.timeout:
self._ready = True
return self._ready class Snow(object):
"""
miniature Web Framework class
"""
def __init__(self, routes):
self.routes = routes
self.inputs = set()
self.request = None
self.async_request_handler = {} def run(self, host='localhost', port=9999):
"""
The event loop
:param host:
:param port:
:return:
"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port,))
sock.setblocking(False) # Non blocking
sock.listen(128)
sock.setblocking(0)
self.inputs.add(sock) # Put this socket Add to inputs In this collection
try:
while True:
readable_list, writeable_list, error_list = select.select(self.inputs, [], self.inputs,0.005) # monitor
for conn in readable_list: # Loop readable collection
if sock == conn: # socket There's a new connection coming in
client, address = conn.accept() # obtain conn、addr
client.setblocking(False)
self.inputs.add(client) # Connect this to conn Join the monitor
else:
gen = self.process(conn) # obtain 、 Deal with connection conn, Take the requester, the requester
if isinstance(gen, HttpResponse): # If you return HttpResponse Type of , The representative is a normal request
conn.sendall(gen.response()) # Send to client
self.inputs.remove(conn) # Now that I'm back , It needs to be removed from the listening queue
conn.close() # Close this socket Connect
else:
yielded = next(gen) # If it's not a normal request, it must be yield future
self.async_request_handler[conn] = yielded # future Objects are added to the dictionary key==》conn,value==》future object
self.polling_callback() # for The loop is finished , Execute this function except Exception as e:
pass
finally:
sock.close() def polling_callback(self):
"""
Traversal triggers asynchronous non blocking callback functions
:return:
"""
for conn in list(self.async_request_handler.keys()): # Recycle key
yielded = self.async_request_handler[conn] # According to the cycle of key take value
# conn:socket object
# yielded:future object
if not yielded.ready: # ready Boolean value , If executed set_result, It becomes true, It means it's ready
continue # Otherwise the cycle continues
if yielded.callback: # If you are set result
ret = yielded.callback(self.request, yielded)
conn.sendall(ret.response()) # send data
self.inputs.remove(conn) # Remove this from monitor conn
del self.async_request_handler[conn] # Delete... From the circular dictionary
conn.close() # close conn def process(self, conn):
"""
Dealing with routing systems and executing functions
:param conn:
:return:
"""
self.request = HttpRequest(conn)
func = None
for route in self.routes:
if re.match(route[0], self.request.url):
func = route[1]
break
if not func:
return HttpNotFound()
else:
return func(self.request)

Custom asynchronous non blocking framework

Use

from snow import Snow
from snow import HttpResponse def index(request):
return HttpResponse('OK') routes = [
(r'/index/', index),
] app = Snow(routes)
app.run(port=8012)

Basic use

from snow import Snow
from snow import HttpResponse
from snow import TimeoutFuture request_list = [] def async(request):
obj = TimeoutFuture(5)
yield obj def home(request):
return HttpResponse('home') routes = [
(r'/home/', home),
(r'/async/', async),
] app = Snow(routes)
app.run(port=8012)

Asynchronous non-blocking : Overtime

from snow import Snow
from snow import HttpResponse
from snow import Future request_list = [] def callback(request, future):
return HttpResponse(future.value) def req(request):
obj = Future(callback=callback)
request_list.append(obj)
yield obj def stop(request):
obj = request_list[0]
del request_list[0]
obj.set_result('done')
return HttpResponse('stop') routes = [
(r'/req/', req),
(r'/stop/', stop),
] app = Snow(routes)
app.run(port=8012)

Asynchronous non-blocking : wait for

Based on the waiting mode, self customization operation can be completed

Ben blog Reference from :https://www.cnblogs.com/wupeiqi/p/6536518.html

Python Tornado More related articles in the introduction

  1. python tornado websocket More chat rooms ( Return the message to part of the connector )

    python tornado Build multiple chat rooms , Multiple chat rooms are independent of each other , Realize that the server will return the message to the corresponding part of the client ! chatHome.py // Server side , Render the home page --> Chat room set up web ...

  2. Python Introduction and installation of the first program and usage

    Python An introduction to the : 1.Python It's an interpretive type . object-oriented . Advanced programming language for dynamic data types . since 20 century 90 s Python Since the birth of language , It is gradually widely used to deal with system management tasks and Web Programming .Pytho ...

  3. [Python] heapq brief introduction

    [Python] heapq brief introduction « Lonely Coder [Python] heapq brief introduction judezhan Published on 2012 year 8 month 8 Japan No comments Comment Suppose you need to maintain a list , this ...

  4. Python.tornado.0

    Tornado https://github.com/facebook/tornado http://www.tornadoweb.org/en/stable/guide/intro.html  (A ...

  5. SpringCloud integrate into Python - Tornado

    Preface This article shares how to Python Web Service integration into Spring Cloud In the micro service system , And call its service ,Python Web The frame uses Tornado structure Python web service introduce py-eure ...

  6. Python+Tornado+Tampermonkey Access to a news and other mainstream video website member video analysis play

    In the near future ,< The devil child of Nezha came into the world > In the major video software can see , But it's a routine , Non members are not allowed to watch !!! I can only find it on the various video websites in China , Or through what I said before Chrome The oil monkey plug-in , Portal   ...

  7. 01-tornado Learning notes -Tornado brief introduction

    01-Tornado brief introduction   Tornado It's using Python Write a powerful . Extensible Web The server . It's robust enough to handle severe network traffic , But it's lightweight enough to create and write , And can be used in a large number of applications ...

  8. python The most powerful of all (1)——python Introduction

    1.Python brief introduction Python It's an interpretive type . object-oriented . Advanced programming language for dynamic data types . Python from Guido van Rossum On 1989 At the end of the year , The first public release was released in 1991 year . image Pe ...

  9. python tornado introduction

    #!/usr/bin/env python # coding:utf-8 import textwrap import tornado.httpserver import tornado.ioloop ...

Random recommendation

  1. Simple understanding ECMAScript2015 Medium Promise

    ECMAScript6 Added in Promise object , So-called Promise object , That is to say, it represents an unfinished project , But what will be done sometime in the future ( It's usually asynchronous ). Use Promise object , We can avoid falling into the nesting of functions ‘ return ...

  2. How to motivate employees - quote LTP.Net The knowledge base

    Maybe the boss doesn't have a strong sense , Maybe employees are weak in consciousness , Is the awareness of the boss strong , We can only see that he only cares about the immediate interests , Or look to the future . 1: There's a leader like that now , In our country , Chatting is very fashionable , It's also very popular with people of our age . Such as ...

  3. Android MotionEvent getX() getY() getRawX() getRawY() and View getTop() getLeft()

    getRowX: The coordinates of the touch point relative to the screen getX: The coordinates of the touch point relative to the button getTop: The top left corner of the button is relative to the parent view(LinerLayout) Of y coordinate getLeft: The top left corner of the button is relative to the parent view(Lin ...

  4. Android senior UI Design notes 02: You can drag the swap item Positional GridView( Reprint )

    If you don't know GridView Basic use , See... First :Android(java) Learning notes 154: Use GridView And rewrite BaseAdapter 1. First of all, we understand GridView The idea of drag and drop : () root ...

  5. Through oscilloscope analysis TypeB Card communication data

    This few days , Use NFC The chip simulates a TypeB Of cpu card , In the course of debugging , Because to check the RF performance , So I grabbed it with an oscilloscope RFID Readers and TypeB CPU Communication data between cards .READER Data bits sent 106K ASK transfer ...

  6. iframe Property parameters

    iframe Property parameters When you click on a link to a subpage , How to embed another sub page into the current iframe in Just give this iframe Just name it . <iframe width=420 height=330 ...

  7. JS-DOM Operation application advanced ( Two )

    Search for String comparison . Ignore case ---- toggle case . Fuzzy search ----search Use .split. Highlight and filter toLowerCase() Method to convert a string to lowercase str.search('')   ...

  8. seek n The least common multiple of the number

    Problem solved : For a length of n Sequence ai, seek ai The least common multiple of analysis : We know , If you find two numbers a,b Of LCM=a*b/gcd(a,b), We can find multiple numbers in pairs LCM, Re merger , It's going to explode long long therefore ...

  9. Graph database -Neo4j Use

    Cypher The query language is simple to use 3.1. Basic grammar Node grammar : Cypher Use a pair of parentheses to represent a node : Various formats are provided as follows : ()  Anonymous nodes (matrix)   Add a ID (:Movie ...

  10. LeetCode 961. N-Repeated Element in Size 2N Array

    In a array A of size 2N, there are N+1 unique elements, and exactly one of these elements is repeate ...