tornado框架学习 - 菜鸟学院


本站和网页 http://www.noobyard.com/article/p-vywafxpz-hn.html 的作者无关,不对其内容负责。快照谨为网络故障时之索引,不代表被搜索网站的即时页面。

tornado框架学习 - 菜鸟学院
菜鸟学院
栏目
标签
tornado框架学习
时间 2019-11-19
标签
tornado
框架
学习
栏目
Python
繁體版
  tornado是一个非阻塞的web服务器框架,每秒能够处理上千个客户端链接(都是在一个线程中,不须要为每一个客户端建立线程,资源消耗少),适合用来开发web长链接应用,如long polling(轮询),WebSocket协议等(http协议为短链接)。javascript
1,简单使用css
#coding:utf-8
import tornado.ioloop
import tornado.web
from controllers.login import LoginHandler
class HomeHandler(tornado.web.RequestHandler): #处理'/index'的请求,如果get请求,即调用get方法
def get(self, *args, **kwargs):
self.write('home page')
settings = {
'template_path':'views' #配置html文件的目录,即html文件存储在views文件夹路径下  'static_path':'statics', # 配置静态url路径,用来存放css,js文件等
app = tornado.web.Application([
(r'/index',HomeHandler), # 路由分发器,HomeHandler为该路由的处理类
(r'/login',LoginHandler),
],**settings) #加入配置文件
if __name__ == '__main__':
app.listen(8080) #监听端口号
tornado.ioloop.IOLoop.instance().start() #开启服务器
  上面代码即创建起一个web服务器,在浏览器输入127.0.0.1:8080/index, 就会获得包含‘home page’字符的网页。另外,上面将全部代码写在了有个代码文件中,也能够利用MVC的设计方式分开来写,以下面的的架构和代码:将处理‘/login’请求的类LoginHandler放在controllers文件夹下,将视图文件login.html放在views文件夹下(须要配置‘template_path’),而models文件夹下能够存放和数据库处理相关的代码,statics中存放静态文件,如css,js等,须要配置路径:'static_path':'statics'。html
#coding:utf-8
import tornado.ioloop
import tornado.web
from controllers.login import LoginHandler
class HomeHandler(tornado.web.RequestHandler): #处理'/index'的请求,如果get请求,即调用get方法
def get(self, *args, **kwargs):
self.write('home page')
settings = {
'template_path':'views' #配置html文件的目录,即html文件存储在views文件夹路径下
app = tornado.web.Application([
(r'/index',HomeHandler), # 路由分发器,HomeHandler为该路由的处理类
(r'/login',LoginHandler),
],**settings) #加入配置文件
if __name__ == '__main__':
app.listen(8080) #监听端口号
tornado.ioloop.IOLoop.instance().start() #开启服务器
app.py
#coding:utf-8
import tornado
class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.render('login.html')
login.py
2.模板前端
  tornado也支持和django相似的模板引擎语言,表达语句用{{ item[0] }},控制语句{% if %}。。。。 {% end %},tornado支持if,while,for,try等,但都是以{% end %}结束,不一样于django。tornado也支持模板继承,{% extends 'index.html' %} 和 {% block body%}。。。。{% end %}(也是以{% end %}结尾)。java
http://www.tornadoweb.org/en/stable/template.htmlpython
https://github.com/tornadoweb/tornado/blob/master/tornado/template.pyjquery
Tornado默认提供的这些功能其实本质上就是 UIMethod 和 UIModule,咱们也能够自定义从而实现相似于Django的simple_tag的功能:git
定义:github
#coding:utf-8
from tornado import escape
def mytag(request,value): #默认会传递一个参数(HomeHandler object),前端须要传值时须要再加一个参数value
#print request
return '<h3>我是tag%s</h3>'%value # 前端默认会对和h3进行转义,须要不转义时前端使用raw 关键字
uimethods.py
#coding:utf-8
from tornado import escape
from tornado.web import UIModule
class CustomUIModule(UIModule):
def embedded_javascript(self): # render执行时,会在html文件中加入javascript
return "console.log(123);"
def javascript_files(self): ## render执行时,会在html文件中引入javascript文件
return 'commons.js'
def embedded_css(self):
return '.but{color:red}'
def css_files(self):
return 'commons.css'
def render(self, value):
v = '<h3>我是一个UIModule tag%s</h3>'%value #默认不转义</h3>,前端显示我是一个UIModule tag3
#v = escape.xhtml_escape(v) # 转义</h3>,前端显示<h3>我是一个UIModule tag3</h3>
return v
uimodules.py
设置:web
#coding:utf-8
import tornado.ioloop
import tornado.web
from controllers.login import LoginHandler
import uimethods
import uimodules
class HomeHandler(tornado.web.RequestHandler): #处理'/index'的请求,如果get请求,即调用get方法
def get(self, *args, **kwargs):
#self.write('home page')
self.render('home.html')
settings = {
'template_path':'views', #配置html文件的目录,即html文件存储在views文件夹路径下
'static_path':'statics', # 配置静态url路径,用来存放css,js文件等
'ui_methods':uimethods,
'ui_modules':uimodules,
app = tornado.web.Application([
(r'/index',HomeHandler), # 路由分发器,HomeHandler为该路由的处理类
(r'/login',LoginHandler),
],**settings) #加入配置文件
if __name__ == '__main__':
app.listen(8080) #监听端口号
tornado.ioloop.IOLoop.instance().start() #开启服务器
app.py
使用
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>主页</title>
</head>
<body>
{{ mytag(1)}}
{% raw mytag(2) %}
{% module CustomUIModule(3) %}
<p class="but">验证css代码</p>
<p class="but2">验证css文件</p>
</body>
</html>
home.html
网页效果:
注意的是在UIModule中能够向html文件中加入css,js代码及文件。
3,静态文件设置
app配置
settings = {
'static_path':'statics', # 配置静态url路径,用来存放css,js文件等
'static_url_prefix':'/statics/', #href中的起始路径
html
<link rel="stylesheet" href="/statics/commons.css"> #statics目录下的commons.css
4. 跨站请求伪造(cross site request forgery)
https://www.tornadoweb.org/en/stable/guide/security.html?highlight=ajax
app设置
settings = {
"xsrf_cookies": True,
表单使用
<form action="/new_message" method="post">
{% module xsrf_form_html() %}
<input type="text" name="message"/>
<input type="submit" value="Post"/>
</form>
ajax使用:
本质上去cookie中获取_xsrf,再携带_xsrf值提交数据(document.cookie:_xsrf=2|160fb996|ce7f56d73e0cbe6c89a74cb0f92db4b2|1541324310)
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 + ")"));
}});
};
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
$('#send').click(function () {
var _xsrf = getCookie('_xsrf')
var msg = $('#msg').val();
$.ajax({
url:'/login',
data:{
'_xsrf':_xsrf,
'msg':msg,
},
type:"POST",
success:function (callback) {
console.log(callback);
});
});
5,ajax上传文件
不用ajax前端
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
</head>
<body>
<div>
<input type="file" id="img"/>
<button onclick="upload();">上传</button>
</div>
</body>
<script src="/statics/jquery-3.3.1.min.js"></script>
<script>
function upload() {
var file = document.getElementById('img').files[0];
var form = new FormData();
//form.append('k1','v1');
form.append('fileobj',file);
var request = new XMLHttpRequest();
request.open('post','/index',true);
request.send(form);
</script>
</html>
View Code
ajax前端
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
</head>
<body>
<div>
<input type="file" id="img"/>
<button onclick="upload();">上传</button>
</div>
</body>
<script src="/statics/jquery-3.3.1.min.js"></script>
<script>
function upload() {
var file = document.getElementById('img').files[0];
var form = new FormData();
//form.append('k1','v1');
form.append('fileobj',file);
//var request = new XMLHttpRequest();
//request.open('post','/index',true);
//request.send(form);
$.ajax({
url:'/index',
type:'POST',
data:form,
processData:false, //让jquery不处理数据
contentType:false, // 让jquery不设置contentType
success:function (callback) {
console.log(callback);
});
</script>
</html>
View Code
后端
#coding:utf-8
import tornado.web
class HomeHandler(tornado.web.RequestHandler):
def get(self):
self.render('LoadFile.html')
def post(self):
fileobjs = self.request.files['fileobj'] #fileobjs为一个列表
for file in fileobjs:
file_name = file['filename'] #fileobjs[0]['filename']
print type(file_name)
with open(file_name,'wb') as f:
f.write(file['body'])
settings={
'template_path':'views',
'static_path':'statics',
'static_url_prefix':'/statics/',
application = tornado.web.Application([
(r'/index', HomeHandler)
],**settings)
if __name__ == '__main__':
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
View Code
6,cookie
获取和设置cookie(不加密):
get_cookie(self, name, default=None): 未取到时返回默认值
def set_cookie(self, name, value, domain=None, expires=None, path="/",expires_days=None, **kwargs):
class HomeHandler(tornado.web.RequestHandler): #处理'/index'的请求,如果get请求,即调用get方法
def get(self, *args, **kwargs):
#self.write('home page')
if self.get_cookie(name='id'):
print self.get_cookie(name='id')
else:
self.set_cookie(name='id',value='asdfg')
self.render('home.html')
View Code
获取和设置cookie(加密):须要在配置中设置秘钥:'cookie_secret'
get_secure_cookie(self, name, value=None, max_age_days=31, min_version=None): 对于加密后的cookie,get_secure_cookie拿到的为解密后的cookie值,get_cookie拿到的为加密的值
set_secure_cookie(self, name, value, expires_days=30, version=None, **kwargs):
class HomeHandler(tornado.web.RequestHandler): #处理'/index'的请求,如果get请求,即调用get方法
def get(self, *args, **kwargs):
if self.get_secure_cookie(name='secret_id'):
print self.get_secure_cookie(name='secret_id') ##前端显示的为加密后,拿到的为明文
else:
self.set_secure_cookie(name='secret_id',value='message')
self.render('home.html')
settings = {
'template_path':'views', #配置html文件的目录,即html文件存储在views文件夹路径下
'static_path':'statics', # 配置静态url路径,用来存放css,js文件等
'static_url_prefix':'/statics/',
'ui_methods':uimethods,
'ui_modules':uimodules,
'xsrf_cookies':True,
'cookie_secret':'asdfghhj',
View Code
cookie两个版本的加密算法:
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())
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"2",
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)
#解密:
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"0"):
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
加密和解密算法
tornado自带的基于cookie的验证机制:
必须重写方法get_current_user(self):,self.current_user()会调用该方法,拿到当前用户
@tornado.web.authenticated,装饰器修饰的请求会要求验证,self.current_user()中拿到值时,能进行访问,无值时跳转到登陆页面(必须进行配置:'login_url':'/login')
#!/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 #须要登陆后才能访问(self.current_user()拿到当前用户),不然跳转到登陆页面
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 == '123':
self.set_secure_cookie('login_user', 'zack')
self.redirect('/')
else:
self.render('login.html', **{'status': '用户名或密码错误'})
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()
View Code
7, 自定义session框架
预备知识一:字典
任何类实现了__getitem__(), __setitem__(), __delitem__()方法,就能向字典同样存取,删除数据
class Adict(object):
def __init__(self):
self.container = {}
def __getitem__(self, key):
print 'get'
if key in self.container:
return self.container[key]
else:
return None
def __setitem__(self, key, value):
print 'set'
self.container[key]=value
def __delitem__(self, key):
print 'del'
del self.container[key]
D = Adict()
D['user']='zack' #调用 __setitem__方法
D['user'] #调用 __getitem__方法
del D['user'] # 调用 __delitem__方法
View Code
预备知识二:类继承
#coding:utf-8
#C实例化时,先调用A的实例化方法,而其会调用self.initialize()时会只执行B的initialize()方法
class A(object):
def __init__(self):
print 'A'
self.initialize()
def initialize(self):
print 'A初始化'
class B(A):
def initialize(self):
print 'B初始化'
class C(B):
pass
c = C()
单继承
#coding:utf-8
#C实例化时,先调用A的实例化方法,而其会调用self.initialize()时会只调用B的initialize()方法,而B的initialize()方法又调用了A的initialize方法
class A(object):
def __init__(self):
print 'A'
self.initialize()
def initialize(self):
print 'A初始化'
class B(object):
def initialize(self):
print 'B初始化'
super(B,self).initialize() #此处super先寻找其父类,没找到,再找A的initialize方法,(先深度,后广度)
class C(B,A):
pass
c = C()
多继承
预备知识三:在RequestHandler的源码中,__init__()函数调用了self.initialize()函数
class RequestHandler(object):
"""Base class for HTTP request handlers.
Subclasses must define at least one of the methods defined in the
"Entry points" section below.
"""
SUPPORTED_METHODS = ("GET", "HEAD", "POST", "DELETE", "PATCH", "PUT",
"OPTIONS")
_template_loaders = {} # type: typing.Dict[str, template.BaseLoader]
_template_loader_lock = threading.Lock()
_remove_control_chars_regex = re.compile(r"[\x00-\x08\x0e-\x1f]")
def __init__(self, application, request, **kwargs):
super(RequestHandler, self).__init__()
self.application = application
self.request = request
self._headers_written = False
self._finished = False
self._auto_finish = True
self._transforms = None # will be set in _execute
self._prepared_future = None
self._headers = None # type: httputil.HTTPHeaders
self.path_args = None
self.path_kwargs = None
self.ui = ObjectDict((n, self._ui_method(m)) for n, m in
application.ui_methods.items())
# UIModules are available as both `modules` and `_tt_modules` in the
# template namespace. Historically only `modules` was available
# but could be clobbered by user additions to the namespace.
# The template {% module %} directive looks in `_tt_modules` to avoid
# possible conflicts.
self.ui["_tt_modules"] = _UIModuleNamespace(self,
application.ui_modules)
self.ui["modules"] = self.ui["_tt_modules"]
self.clear()
self.request.connection.set_close_callback(self.on_connection_close)
self.initialize(**kwargs)
def initialize(self):
"""Hook for subclass initialization. Called for each request.
A dictionary passed as the third argument of a url spec will be
supplied as keyword arguments to initialize().
Example::
class ProfileHandler(RequestHandler):
def initialize(self, database):
self.database = database
def get(self, username):
...
app = Application([
(r'/user/(.*)', ProfileHandler, dict(database=database)),
])
"""
pass
源码
自定义session框架
#coding:utf-8
import tornado.ioloop
import tornado.web
from hashlib import sha1
import time
import os
container={}
create_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()
class Session(object): #一个类实现了__setitem__,__getitem__就能够向字典同样读取和存取数据
session_id='session_id'
def __init__(self,request):
session_value = request.get_cookie(Session.session_id,None)
if not session_value:
self._id = create_session_id()
else:
if session_value in container:
self._id=session_value
else:
self._id = create_session_id()
request.set_cookie(Session.session_id,self._id)
if self._id not in container:
container[self._id]={}
def __setitem__(self, key, value):
container[self._id][key]=value
print container
def __getitem__(self, key):
if key in container[self._id]:
return container[self._id][key]
else:
return None
def __delitem__(self, key):
del container[self._id][key]
def clear(self):
del container[self._id]
# class BaseHandler(object):
# def initialize(self):
# self.session = Session(self)
# super(BaseHandler,self).initialize() #不会覆盖tornado.web.RequestHandler的initialiaze方法
# class HomeHandler(BaseHandler,tornado.web.RequestHandler):
class BaseHandler(tornado.web.RequestHandler):
def initialize(self): # 覆盖tornado.web.RequestHandler的initialiaze方法,初始化时父类中会调用该方法
self.session = Session(self)
class HomeHandler(BaseHandler):
def get(self):
user = self.session['user']
if user:
self.write(user)
else:
self.redirect('/login')
class LoginHandler(BaseHandler):
def get(self):
self.render('login.html')
def post(self):
username = self.get_body_argument('username')
password = self.get_body_argument('password')
if username=='zack' and password=='1234':
self.session['user']='zack'
self.session['pwd']='1234'
self.redirect('/index')
else:
self.render('login.html')
settings={
'template_path':'views'
application = tornado.web.Application([
(r'/index', HomeHandler),
(r'/login', LoginHandler),
],**settings)
if __name__ == '__main__':
application.listen(9999)
tornado.ioloop.IOLoop.instance().start()
session框架
8,异步非阻塞
http://www.tornadoweb.org/en/stable/guide/async.html
  上面都是利用tornado的同步访问请求,当一个请求被阻塞时,下一个请求访问时不能被处理。以下面代码,当先访问‘/mani’时,因为MainHandler中,get方法sleep会阻塞在此处,此时若访问‘/page’,也会阻塞,等待MainHandler中get方法执行完成后,才会执行PageHandler中的get方法。
#coding:utf-8
import tornado.web
import tornado.ioloop
from tornado.concurrent import Future
import time
class MainHandler(tornado.web.RequestHandler):
def get(self):
time.sleep(10)
self.write('main')
class PageHandler(tornado.web.RequestHandler):
def get(self):
self.write('page')
application = tornado.web.Application([
(r'/main',MainHandler),
(r'/page',PageHandler)
])
if __name__ == '__main__':
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
同步阻塞
  tornado中,利用装饰器@gen.coroutine +yield Future对象,来支持异步非阻塞。以下面代码,当给MainHandler中get方法加上装饰器@gen.coroutine,并返回Future对象时,就变成了异步非阻塞,也就是说,当咱们先访问‘/mani’时,MainHandler中get方法会阻塞在这里,但当咱们此时去访问访问‘/page’,PageHandler中的get方法会当即执行,而不会阻塞。
#coding:utf-8
import tornado.web
import tornado.ioloop
from tornado import gen
from tornado.concurrent import Future
import time
class MainHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
future = Future()
yield future
self.write('main')
class PageHandler(tornado.web.RequestHandler):
def get(self):
self.write('page')
application = tornado.web.Application([
(r'/main',MainHandler),
(r'/page',PageHandler)
])
if __name__ == '__main__':
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
异步非阻塞
  上面写的异步非阻塞并没实际用途,下面是它的一个应用场景,在代码中,MainHandler的get方法中,fetch()比较耗时,但其返回一Future对象,当咱们先访问‘/mani’时,MainHandler中get方法会阻塞在这里,但当咱们此时去访问访问‘/page’,PageHandler中的get方法会当即执行
#coding:utf-8
import tornado.web
import tornado.ioloop
from tornado import gen, httpclient
from tornado.concurrent import Future
class MainHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
http = httpclient.AsyncHTTPClient() #发送异步请求
data = yield http.fetch('https://www.youtube.com/',raise_error=False) #其源码中能够看到return future,即返回future对象
print 'done',data
self.write('main')
self.finish('dd')
# 加入回调函数处理
# @gen.coroutine
# def get(self):
# http = httpclient.AsyncHTTPClient() #发送异步请求
# yield http.fetch('https://www.youtube.com/',callback=self.done,raise_error=False) #其源码中能够看到return future,即返回future对象
# def done(self,response):
# print 'done',response
# self.write('main')
# self.finish('dd')
class PageHandler(tornado.web.RequestHandler):
def get(self):
self.write('page')
application = tornado.web.Application([
(r'/main',MainHandler),
(r'/page',PageHandler)
])
if __name__ == '__main__':
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
View Code
  从python 3.5 开始,关键字async 和 await能够用来代替@gen.coroutine +yield,代码以下:
http://www.tornadoweb.org/en/stable/guide/coroutines.html
async def fetch_coroutine(url):
http_client = AsyncHTTPClient()
response = await http_client.fetch(url)
return response.body
'''
# Decorated: # Native:
# Normal function declaration
# with decorator # "async def" keywords
@gen.coroutine
def a(): async def a():
# "yield" all async funcs # "await" all async funcs
b = yield c() b = await c()
# "return" and "yield"
# cannot be mixed in
# Python 2, so raise a
# special exception. # Return normally
raise gen.Return(b) return b
'''
View Code
  其实现异步阻塞的关键在于Future对象,下面是其部分源码,能够看到其_result属性初始化没有值,tornado内部会监听每个Future对象的_result属性值,若没有值时,继续阻塞,如有值时,若某个Future对象的_result属性值有值了,处理该请求,结束阻塞,继续监听其余Future对象。
关于Future类能够参考:https://www.cnblogs.com/silence-cho/p/9867499.html
class Future(object):
"""Represents the result of an asynchronous computation."""
def __init__(self):
"""Initializes the future. Should not be called by clients."""
self._condition = threading.Condition()
self._state = PENDING
self._result = None
self._exception = None
self._traceback = None
self._waiters = []
self._done_callbacks = []
参考文章:
官方文档:http://www.tornadoweb.org/en/stable/index.html
http://www.cnblogs.com/wupeiqi/articles/5341480.html
http://www.cnblogs.com/wupeiqi/articles/5702910.html
http://www.cnblogs.com/wupeiqi/p/6536518.html
相关文章
1. Python web框架 Tornado(一)基础学习
2. tornado框架
3. Tornado框架
4. Tornado 框架
5. Tornado Web 框架
6. tornado学习笔记19 Tornado框架分析
7. Python Web 框架:Tornado
8. WEB框架之Tornado
9. Tornado框架04-cookie
10. tornado框架——入门
更多相关文章...
相关标签/搜索
Hibernate框架学习
框架
tornado
架构学习
中止学习框架
ABP框架系列学习
驳 《中止学习框架
PHP框架Laravel学习
深度学习框架
深度学习框架Keras
Python
分享到微博
分享到微信
分享到QQ
每日一句
每一个你不满意的现在,都有一个你没有努力的曾经。
最新文章
1.
MCMC、吉布斯采样
2.
弘辽科技:一个人是否可以开起淘宝店
3.
java面试知识点总结1
4.
易企秀资深前端架构师袁飞分享:移动H5开发如何避坑?
5.
cocos creator 使用wx的API提示找不到wx的问题
6.
嘉宾专访|2020 PostgreSQL亚洲大会阿里云数据库专场:樊智辉
7.
关于Mac睡眠模式,你不知道的那些事
8.
记录日常代码延时高的问题
9.
蚂蚁金服P7首次公开自己 十年架构师成长匠心之作【学习路线分享】
10.
CakePHP v3.9.3 Entity
相关文章
1.
Python web框架 Tornado(一)基础学习
2.
tornado框架
3.
Tornado框架
4.
Tornado 框架
5.
Tornado Web 框架
6.
tornado学习笔记19 Tornado框架分析
7.
Python Web 框架:Tornado
8.
WEB框架之Tornado
9.
Tornado框架04-cookie
10.
tornado框架——入门
>>更多相关文章<<
联系我们
最近搜索
最新文章
沪ICP备2021010478号-7