tornado是一个非阻塞的web服务器框架,每秒可以处理上千个客户端连接(都是在一个线程中,不需要为每个客户端创建线程,资源消耗少),适合用来开发web长连接应用,如long polling(轮询),WebSocket协议等(http协议为短连接)。

1,简单使用

  1. #coding:utf-8
  2. import tornado.ioloop
  3. import tornado.web
  4. from controllers.login import LoginHandler
  5.  
  6. class HomeHandler(tornado.web.RequestHandler): #处理'/index'的请求,若是get请求,即调用get方法
  7. def get(self, *args, **kwargs):
  8. self.write('home page')
  9.  
  10. settings = {
  11. 'template_path':'views' #配置html文件的目录,即html文件存储在views文件夹路径下
      'static_path':'statics', # 配置静态url路径,用来存放css,js文件等
  12. }
  13. app = tornado.web.Application([
  14. (r'/index',HomeHandler), # 路由分发器,HomeHandler为该路由的处理类
  15. (r'/login',LoginHandler),
  16. ],**settings) #加入配置文件
  17.  
  18. if __name__ == '__main__':
  19. app.listen(8080) #监听端口号
  20. 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'。

  1. #coding:utf-8
  2.  
  3. import tornado.ioloop
  4. import tornado.web
  5. from controllers.login import LoginHandler
  6.  
  7. class HomeHandler(tornado.web.RequestHandler): #处理'/index'的请求,若是get请求,即调用get方法
  8. def get(self, *args, **kwargs):
  9. self.write('home page')
  10.  
  11. settings = {
  12. 'template_path':'views' #配置html文件的目录,即html文件存储在views文件夹路径下
  13. }
  14. app = tornado.web.Application([
  15. (r'/index',HomeHandler), # 路由分发器,HomeHandler为该路由的处理类
  16. (r'/login',LoginHandler),
  17. ],**settings) #加入配置文件
  18.  
  19. if __name__ == '__main__':
  20. app.listen(8080) #监听端口号
  21. tornado.ioloop.IOLoop.instance().start() #开启服务器

app.py

  1. #coding:utf-8
  2.  
  3. import tornado
  4.  
  5. class LoginHandler(tornado.web.RequestHandler):
  6.  
  7. def get(self):
  8. 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 %}结尾)。

http://www.tornadoweb.org/en/stable/template.html

https://github.com/tornadoweb/tornado/blob/master/tornado/template.py

Tornado默认提供的这些功能其实本质上就是 UIMethod 和 UIModule,我们也可以自定义从而实现类似于Django的simple_tag的功能:

定义:

  1. #coding:utf-8
  2. from tornado import escape
  3.  
  4. def mytag(request,value): #默认会传递一个参数(HomeHandler object),前端需要传值时需要再加一个参数value
  5. #print request
  6. return '<h3>我是tag%s</h3>'%value # 前端默认会对和h3进行转义,需要不转义时前端使用raw 关键字

uimethods.py

  1. #coding:utf-8
  2. from tornado import escape
  3. from tornado.web import UIModule
  4.  
  5. class CustomUIModule(UIModule):
  6. def embedded_javascript(self): # render执行时,会在html文件中加入javascript
  7. return "console.log(123);"
  8. def javascript_files(self): ## render执行时,会在html文件中引入javascript文件
  9. return 'commons.js'
  10. def embedded_css(self):
  11. return '.but{color:red}'
  12. def css_files(self):
  13. return 'commons.css'
  14. def render(self, value):
  15. v = '<h3>我是一个UIModule tag%s</h3>'%value #默认不转义</h3>,前端显示我是一个UIModule tag3
  16. #v = escape.xhtml_escape(v) # 转义</h3>,前端显示<h3>我是一个UIModule tag3</h3>
  17. return v

uimodules.py

设置:

  1. #coding:utf-8
  2.  
  3. import tornado.ioloop
  4. import tornado.web
  5. from controllers.login import LoginHandler
  6. import uimethods
  7. import uimodules
  8.  
  9. class HomeHandler(tornado.web.RequestHandler): #处理'/index'的请求,若是get请求,即调用get方法
  10. def get(self, *args, **kwargs):
  11. #self.write('home page')
  12. self.render('home.html')
  13.  
  14. settings = {
  15. 'template_path':'views', #配置html文件的目录,即html文件存储在views文件夹路径下
  16. 'static_path':'statics', # 配置静态url路径,用来存放css,js文件等
  17. 'ui_methods':uimethods,
  18. 'ui_modules':uimodules,
  19. }
  20. app = tornado.web.Application([
  21. (r'/index',HomeHandler), # 路由分发器,HomeHandler为该路由的处理类
  22. (r'/login',LoginHandler),
  23. ],**settings) #加入配置文件
  24.  
  25. if __name__ == '__main__':
  26. app.listen(8080) #监听端口号
  27. tornado.ioloop.IOLoop.instance().start() #开启服务器

app.py

使用

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <title>主页</title>
  8. </head>
  9. <body>
  10. {{ mytag(1)}}
  11. {% raw mytag(2) %}
  12. {% module CustomUIModule(3) %}
  13. <p class="but">验证css代码</p>
  14. <p class="but2">验证css文件</p>
  15.  
  16. </body>
  17. </html>

home.html

网页效果:

注意的是在UIModule中可以向html文件中加入css,js代码及文件。

 3,静态文件设置

app配置

  1. settings = {
  2.  
  3. 'static_path':'statics', # 配置静态url路径,用来存放css,js文件等
  4. 'static_url_prefix':'/statics/', #href中的起始路径
  5. }

html

  1. <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设置

  1. settings = {
  2. "xsrf_cookies": True,
  3. }

表单使用

  1. <form action="/new_message" method="post">
  2. {% module xsrf_form_html() %}
  3. <input type="text" name="message"/>
  4. <input type="submit" value="Post"/>
  5. </form>

ajax使用:

本质上去cookie中获取_xsrf,再携带_xsrf值提交数据(document.cookie:_xsrf=2|160fb996|ce7f56d73e0cbe6c89a74cb0f92db4b2|1541324310)

  1. function getCookie(name) {
  2. var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
  3. return r ? r[1] : undefined;
  4. }
  5. jQuery.postJSON = function(url, args, callback) {
  6. args._xsrf = getCookie("_xsrf");
  7. $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
  8. success: function(response) {
  9. callback(eval("(" + response + ")"));
  10. }});
  11. };
  1. function getCookie(name) {
  2. var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
  3. return r ? r[1] : undefined;
  4. }
  5. $('#send').click(function () {
  6. var _xsrf = getCookie('_xsrf')
  7. var msg = $('#msg').val();
  8. $.ajax({
  9. url:'/login',
  10. data:{
  11. '_xsrf':_xsrf,
  12. 'msg':msg,
  13. },
  14. type:"POST",
  15. success:function (callback) {
  16. console.log(callback);
  17. }
  18. });
  19.  
  20. });

5,ajax上传文件

不用ajax前端

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <title>Title</title>
  8. </head>
  9. <body>
  10. <div>
  11. <input type="file" id="img"/>
  12. <button onclick="upload();">上传</button>
  13. </div>
  14.  
  15. </body>
  16. <script src="/statics/jquery-3.3.1.min.js"></script>
  17. <script>
  18. function upload() {
  19. var file = document.getElementById('img').files[0];
  20. var form = new FormData();
  21. //form.append('k1','v1');
  22. form.append('fileobj',file);
  23. var request = new XMLHttpRequest();
  24. request.open('post','/index',true);
  25. request.send(form);
  26. }
  27. </script>
  28. </html>

ajax前端

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <title>Title</title>
  8. </head>
  9. <body>
  10. <div>
  11. <input type="file" id="img"/>
  12. <button onclick="upload();">上传</button>
  13. </div>
  14.  
  15. </body>
  16. <script src="/statics/jquery-3.3.1.min.js"></script>
  17. <script>
  18. function upload() {
  19. var file = document.getElementById('img').files[0];
  20. var form = new FormData();
  21. //form.append('k1','v1');
  22. form.append('fileobj',file);
  23. //var request = new XMLHttpRequest();
  24. //request.open('post','/index',true);
  25. //request.send(form);
  26. $.ajax({
  27. url:'/index',
  28. type:'POST',
  29. data:form,
  30. processData:false, //让jquery不处理数据
  31. contentType:false, // 让jquery不设置contentType
  32. success:function (callback) {
  33. console.log(callback);
  34. }
  35. });
  36. }
  37.  
  38. </script>
  39. </html>

后端

  1. #coding:utf-8
  2.  
  3. import tornado.web
  4.  
  5. class HomeHandler(tornado.web.RequestHandler):
  6.  
  7. def get(self):
  8.  
  9. self.render('LoadFile.html')
  10. def post(self):
  11. fileobjs = self.request.files['fileobj'] #fileobjs为一个列表
  12. for file in fileobjs:
  13. file_name = file['filename'] #fileobjs[0]['filename']
  14. print type(file_name)
  15. with open(file_name,'wb') as f:
  16. f.write(file['body'])
  17.  
  18. settings={
  19. 'template_path':'views',
  20. 'static_path':'statics',
  21. 'static_url_prefix':'/statics/',
  22. }
  23.  
  24. application = tornado.web.Application([
  25. (r'/index', HomeHandler)
  26. ],**settings)
  27.  
  28. if __name__ == '__main__':
  29. application.listen(8888)
  30. tornado.ioloop.IOLoop.instance().start()

6,cookie

获取和设置cookie(不加密):

  1. get_cookie(self, name, default=None): 未取到时返回默认值
  1. def set_cookie(self, name, value, domain=None, expires=None, path="/",expires_days=None, **kwargs):
  1. class HomeHandler(tornado.web.RequestHandler): #处理'/index'的请求,若是get请求,即调用get方法
  2. def get(self, *args, **kwargs):
  3. #self.write('home page')
  4. if self.get_cookie(name='id'):
  5. print self.get_cookie(name='id')
  6. else:
  7. self.set_cookie(name='id',value='asdfg')
  8. self.render('home.html')

获取和设置cookie(加密):需要在配置中设置秘钥:'cookie_secret'

  1. get_secure_cookie(self, name, value=None, max_age_days=31, min_version=None): 对于加密后的cookieget_secure_cookie拿到的为解密后的cookie值,get_cookie拿到的为加密的值
  1. set_secure_cookie(self, name, value, expires_days=30, version=None, **kwargs):
  1. class HomeHandler(tornado.web.RequestHandler): #处理'/index'的请求,若是get请求,即调用get方法
  2. def get(self, *args, **kwargs):
  3. if self.get_secure_cookie(name='secret_id'):
  4. print self.get_secure_cookie(name='secret_id') ##前端显示的为加密后,拿到的为明文
  5. else:
  6. self.set_secure_cookie(name='secret_id',value='message')
  7.  
  8. self.render('home.html')
  9.  
  10. settings = {
  11. 'template_path':'views', #配置html文件的目录,即html文件存储在views文件夹路径下
  12. 'static_path':'statics', # 配置静态url路径,用来存放css,js文件等
  13. 'static_url_prefix':'/statics/',
  14. 'ui_methods':uimethods,
  15. 'ui_modules':uimodules,
  16. 'xsrf_cookies':True,
  17. 'cookie_secret':'asdfghhj',
  18. }

cookie两个版本的加密算法:

  1. def _create_signature_v1(secret, *parts):
  2. hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
  3. for part in parts:
  4. hash.update(utf8(part))
  5. return utf8(hash.hexdigest())
  6.  
  7. def _create_signature_v2(secret, s):
  8. hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)
  9. hash.update(utf8(s))
  10. return utf8(hash.hexdigest())
  1. #加密
  2. def create_signed_value(secret, name, value, version=None, clock=None,
  3. key_version=None):
  4. if version is None:
  5. version = DEFAULT_SIGNED_VALUE_VERSION
  6. if clock is None:
  7. clock = time.time
  8.  
  9. timestamp = utf8(str(int(clock())))
  10. value = base64.b64encode(utf8(value))
  11. if version == 1:
  12. signature = _create_signature_v1(secret, name, value, timestamp)
  13. value = b"|".join([value, timestamp, signature])
  14. return value
  15. elif version == 2:
  16. # The v2 format consists of a version number and a series of
  17. # length-prefixed fields "%d:%s", the last of which is a
  18. # signature, all separated by pipes. All numbers are in
  19. # decimal format with no leading zeros. The signature is an
  20. # HMAC-SHA256 of the whole string up to that point, including
  21. # the final pipe.
  22. #
  23. # The fields are:
  24. # - format version (i.e. 2; no length prefix)
  25. # - key version (integer, default is 0)
  26. # - timestamp (integer seconds since epoch)
  27. # - name (not encoded; assumed to be ~alphanumeric)
  28. # - value (base64-encoded)
  29. # - signature (hex-encoded; no length prefix)
  30. def format_field(s):
  31. return utf8("%d:" % len(s)) + utf8(s)
  32. to_sign = b"|".join([
  33. b"",
  34. format_field(str(key_version or 0)),
  35. format_field(timestamp),
  36. format_field(name),
  37. format_field(value),
  38. b''])
  39.  
  40. if isinstance(secret, dict):
  41. assert key_version is not None, 'Key version must be set when sign key dict is used'
  42. assert version >= 2, 'Version must be at least 2 for key version support'
  43. secret = secret[key_version]
  44.  
  45. signature = _create_signature_v2(secret, to_sign)
  46. return to_sign + signature
  47. else:
  48. raise ValueError("Unsupported version %d" % version)
  49. #解密:
  50. def _decode_signed_value_v1(secret, name, value, max_age_days, clock):
  51. parts = utf8(value).split(b"|")
  52. if len(parts) != 3:
  53. return None
  54. signature = _create_signature_v1(secret, name, parts[0], parts[1])
  55. if not _time_independent_equals(parts[2], signature):
  56. gen_log.warning("Invalid cookie signature %r", value)
  57. return None
  58. timestamp = int(parts[1])
  59. if timestamp < clock() - max_age_days * 86400:
  60. gen_log.warning("Expired cookie %r", value)
  61. return None
  62. if timestamp > clock() + 31 * 86400:
  63. # _cookie_signature does not hash a delimiter between the
  64. # parts of the cookie, so an attacker could transfer trailing
  65. # digits from the payload to the timestamp without altering the
  66. # signature. For backwards compatibility, sanity-check timestamp
  67. # here instead of modifying _cookie_signature.
  68. gen_log.warning("Cookie timestamp in future; possible tampering %r",
  69. value)
  70. return None
  71. if parts[1].startswith(b""):
  72. gen_log.warning("Tampered cookie %r", value)
  73. return None
  74. try:
  75. return base64.b64decode(parts[0])
  76. except Exception:
  77. return None
  78.  
  79. def _decode_fields_v2(value):
  80. def _consume_field(s):
  81. length, _, rest = s.partition(b':')
  82. n = int(length)
  83. field_value = rest[:n]
  84. # In python 3, indexing bytes returns small integers; we must
  85. # use a slice to get a byte string as in python 2.
  86. if rest[n:n + 1] != b'|':
  87. raise ValueError("malformed v2 signed value field")
  88. rest = rest[n + 1:]
  89. return field_value, rest
  90.  
  91. rest = value[2:] # remove version number
  92. key_version, rest = _consume_field(rest)
  93. timestamp, rest = _consume_field(rest)
  94. name_field, rest = _consume_field(rest)
  95. value_field, passed_sig = _consume_field(rest)
  96. return int(key_version), timestamp, name_field, value_field, passed_sig
  97.  
  98. def _decode_signed_value_v2(secret, name, value, max_age_days, clock):
  99. try:
  100. key_version, timestamp, name_field, value_field, passed_sig = _decode_fields_v2(value)
  101. except ValueError:
  102. return None
  103. signed_string = value[:-len(passed_sig)]
  104.  
  105. if isinstance(secret, dict):
  106. try:
  107. secret = secret[key_version]
  108. except KeyError:
  109. return None
  110.  
  111. expected_sig = _create_signature_v2(secret, signed_string)
  112. if not _time_independent_equals(passed_sig, expected_sig):
  113. return None
  114. if name_field != utf8(name):
  115. return None
  116. timestamp = int(timestamp)
  117. if timestamp < clock() - max_age_days * 86400:
  118. # The signature has expired.
  119. return None
  120. try:
  121. return base64.b64decode(value_field)
  122. except Exception:
  123. return None
  124.  
  125. def get_signature_key_version(value):
  126. value = utf8(value)
  127. version = _get_version(value)
  128. if version < 2:
  129. return None
  130. try:
  131. key_version, _, _, _, _ = _decode_fields_v2(value)
  132. except ValueError:
  133. return None
  134.  
  135. return key_version

加密和解密算法

tornado自带的基于cookie的验证机制:

  1. 必须重写方法get_current_user(self):,self.current_user()会调用该方法,拿到当前用户
  1. @tornado.web.authenticated,装饰器修饰的请求会要求验证,self.current_user()中拿到值时,能进行访问,无值时跳转到登录页面(必须进行配置:'login_url':'/login'
  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. import tornado.ioloop
  5. import tornado.web
  6.  
  7. class BaseHandler(tornado.web.RequestHandler):
  8.  
  9. def get_current_user(self):
  10. return self.get_secure_cookie("login_user")
  11.  
  12. class MainHandler(BaseHandler):
  13.  
  14. @tornado.web.authenticated #需要登录后才能访问(self.current_user()拿到当前用户),否则跳转到登录页面
  15. def get(self):
  16. login_user = self.current_user
  17. self.write(login_user)
  18.  
  19. class LoginHandler(tornado.web.RequestHandler):
  20. def get(self):
  21. self.current_user()
  22.  
  23. self.render('login.html', **{'status': ''})
  24.  
  25. def post(self, *args, **kwargs):
  26.  
  27. username = self.get_argument('name')
  28. password = self.get_argument('pwd')
  29. if username == 'wupeiqi' and password == '':
  30. self.set_secure_cookie('login_user', 'zack')
  31. self.redirect('/')
  32. else:
  33. self.render('login.html', **{'status': '用户名或密码错误'})
  34.  
  35. settings = {
  36. 'template_path': 'template',
  37. 'static_path': 'static',
  38. 'static_url_prefix': '/static/',
  39. 'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
  40. 'login_url': '/login'
  41. }
  42.  
  43. application = tornado.web.Application([
  44. (r"/index", MainHandler),
  45. (r"/login", LoginHandler),
  46. ], **settings)
  47.  
  48. if __name__ == "__main__":
  49. application.listen(8888)
  50. tornado.ioloop.IOLoop.instance().start()

7, 自定义session框架

预备知识一:字典

任何类实现了__getitem__(), __setitem__(), __delitem__()方法,就能向字典一样存取,删除数据

  1. class Adict(object):
  2. def __init__(self):
  3. self.container = {}
  4.  
  5. def __getitem__(self, key):
  6. print 'get'
  7. if key in self.container:
  8. return self.container[key]
  9. else:
  10. return None
  11. def __setitem__(self, key, value):
  12. print 'set'
  13. self.container[key]=value
  14. def __delitem__(self, key):
  15. print 'del'
  16. del self.container[key]
  17.  
  18. D = Adict()
  19.  
  20. D['user']='zack' #调用 __setitem__方法
  21. D['user'] #调用 __getitem__方法
  22. del D['user'] # 调用 __delitem__方法

预备知识二:类继承

  1. #coding:utf-8
  2. #C实例化时,先调用A的实例化方法,而其会调用self.initialize()时会只执行B的initialize()方法
  3. class A(object):
  4. def __init__(self):
  5. print 'A'
  6. self.initialize()
  7.  
  8. def initialize(self):
  9. print 'A初始化'
  10.  
  11. class B(A):
  12.  
  13. def initialize(self):
  14. print 'B初始化'
  15.  
  16. class C(B):
  17. pass
  18.  
  19. c = C()

单继承

  1. #coding:utf-8
  2. #C实例化时,先调用A的实例化方法,而其会调用self.initialize()时会只调用B的initialize()方法,而B的initialize()方法又调用了A的initialize方法
  3. class A(object):
  4. def __init__(self):
  5. print 'A'
  6. self.initialize()
  7.  
  8. def initialize(self):
  9. print 'A初始化'
  10.  
  11. class B(object):
  12.  
  13. def initialize(self):
  14. print 'B初始化'
  15. super(B,self).initialize() #此处super先寻找其父类,没找到,再找A的initialize方法,(先深度,后广度)
  16.  
  17. class C(B,A):
  18. pass
  19.  
  20. c = C()

多继承

预备知识三:在RequestHandler的源码中,__init__()函数调用了self.initialize()函数

  1. class RequestHandler(object):
  2. """Base class for HTTP request handlers.
  3.  
  4. Subclasses must define at least one of the methods defined in the
  5. "Entry points" section below.
  6. """
  7. SUPPORTED_METHODS = ("GET", "HEAD", "POST", "DELETE", "PATCH", "PUT",
  8. "OPTIONS")
  9.  
  10. _template_loaders = {} # type: typing.Dict[str, template.BaseLoader]
  11. _template_loader_lock = threading.Lock()
  12. _remove_control_chars_regex = re.compile(r"[\x00-\x08\x0e-\x1f]")
  13.  
  14. def __init__(self, application, request, **kwargs):
  15. super(RequestHandler, self).__init__()
  16.  
  17. self.application = application
  18. self.request = request
  19. self._headers_written = False
  20. self._finished = False
  21. self._auto_finish = True
  22. self._transforms = None # will be set in _execute
  23. self._prepared_future = None
  24. self._headers = None # type: httputil.HTTPHeaders
  25. self.path_args = None
  26. self.path_kwargs = None
  27. self.ui = ObjectDict((n, self._ui_method(m)) for n, m in
  28. application.ui_methods.items())
  29. # UIModules are available as both `modules` and `_tt_modules` in the
  30. # template namespace. Historically only `modules` was available
  31. # but could be clobbered by user additions to the namespace.
  32. # The template {% module %} directive looks in `_tt_modules` to avoid
  33. # possible conflicts.
  34. self.ui["_tt_modules"] = _UIModuleNamespace(self,
  35. application.ui_modules)
  36. self.ui["modules"] = self.ui["_tt_modules"]
  37. self.clear()
  38. self.request.connection.set_close_callback(self.on_connection_close)
  39. self.initialize(**kwargs)
  40.  
  41. def initialize(self):
  42. """Hook for subclass initialization. Called for each request.
  43.  
  44. A dictionary passed as the third argument of a url spec will be
  45. supplied as keyword arguments to initialize().
  46.  
  47. Example::
  48.  
  49. class ProfileHandler(RequestHandler):
  50. def initialize(self, database):
  51. self.database = database
  52.  
  53. def get(self, username):
  54. ...
  55.  
  56. app = Application([
  57. (r'/user/(.*)', ProfileHandler, dict(database=database)),
  58. ])
  59. """
  60. pass

源码

自定义session框架

  1. #coding:utf-8
  2.  
  3. import tornado.ioloop
  4. import tornado.web
  5. from hashlib import sha1
  6. import time
  7. import os
  8.  
  9. container={}
  10. create_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()
  11.  
  12. class Session(object): #一个类实现了__setitem__,__getitem__就可以向字典一样读取和存取数据
  13.  
  14. session_id='session_id'
  15. def __init__(self,request):
  16. session_value = request.get_cookie(Session.session_id,None)
  17. if not session_value:
  18. self._id = create_session_id()
  19. else:
  20. if session_value in container:
  21. self._id=session_value
  22. else:
  23. self._id = create_session_id()
  24. request.set_cookie(Session.session_id,self._id)
  25. if self._id not in container:
  26. container[self._id]={}
  27.  
  28. def __setitem__(self, key, value):
  29.  
  30. container[self._id][key]=value
  31.  
  32. print container
  33. def __getitem__(self, key):
  34. if key in container[self._id]:
  35. return container[self._id][key]
  36. else:
  37. return None
  38.  
  39. def __delitem__(self, key):
  40. del container[self._id][key]
  41.  
  42. def clear(self):
  43. del container[self._id]
  44.  
  45. # class BaseHandler(object):
  46. # def initialize(self):
  47. # self.session = Session(self)
  48. # super(BaseHandler,self).initialize() #不会覆盖tornado.web.RequestHandler的initialiaze方法
  49. #
  50. # class HomeHandler(BaseHandler,tornado.web.RequestHandler):
  51. #
  52.  
  53. class BaseHandler(tornado.web.RequestHandler):
  54. def initialize(self): # 覆盖tornado.web.RequestHandler的initialiaze方法,初始化时父类中会调用该方法
  55. self.session = Session(self)
  56.  
  57. class HomeHandler(BaseHandler):
  58.  
  59. def get(self):
  60. user = self.session['user']
  61. if user:
  62. self.write(user)
  63. else:
  64. self.redirect('/login')
  65.  
  66. class LoginHandler(BaseHandler):
  67. def get(self):
  68. self.render('login.html')
  69.  
  70. def post(self):
  71. username = self.get_body_argument('username')
  72. password = self.get_body_argument('password')
  73. if username=='zack' and password=='':
  74. self.session['user']='zack'
  75. self.session['pwd']=''
  76. self.redirect('/index')
  77. else:
  78. self.render('login.html')
  79.  
  80. settings={
  81. 'template_path':'views'
  82. }
  83. application = tornado.web.Application([
  84. (r'/index', HomeHandler),
  85. (r'/login', LoginHandler),
  86. ],**settings)
  87.  
  88. if __name__ == '__main__':
  89. application.listen(9999)
  90. 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方法。

  1. #coding:utf-8
  2.  
  3. import tornado.web
  4. import tornado.ioloop
  5. from tornado.concurrent import Future
  6. import time
  7.  
  8. class MainHandler(tornado.web.RequestHandler):
  9.  
  10. def get(self):
  11. time.sleep(10)
  12. self.write('main')
  13.  
  14. class PageHandler(tornado.web.RequestHandler):
  15.  
  16. def get(self):
  17. self.write('page')
  18.  
  19. application = tornado.web.Application([
  20. (r'/main',MainHandler),
  21. (r'/page',PageHandler)
  22. ])
  23.  
  24. if __name__ == '__main__':
  25. application.listen(8888)
  26. tornado.ioloop.IOLoop.instance().start()

同步阻塞

  tornado中,利用装饰器@gen.coroutine +yield Future对象,来支持异步非阻塞。如下面代码,当给MainHandler中get方法加上装饰器@gen.coroutine,并返回Future对象时,就变成了异步非阻塞,也就是说,当我们先访问‘/mani’时,MainHandler中get方法会阻塞在这里,但当我们此时去访问访问‘/page’,PageHandler中的get方法会立即执行,而不会阻塞。

  1. #coding:utf-8
  2.  
  3. import tornado.web
  4. import tornado.ioloop
  5. from tornado import gen
  6. from tornado.concurrent import Future
  7. import time
  8.  
  9. class MainHandler(tornado.web.RequestHandler):
  10.  
  11. @gen.coroutine
  12. def get(self):
  13. future = Future()
  14. yield future
  15. self.write('main')
  16.  
  17. class PageHandler(tornado.web.RequestHandler):
  18.  
  19. def get(self):
  20. self.write('page')
  21.  
  22. application = tornado.web.Application([
  23. (r'/main',MainHandler),
  24. (r'/page',PageHandler)
  25. ])
  26.  
  27. if __name__ == '__main__':
  28. application.listen(8888)
  29. tornado.ioloop.IOLoop.instance().start()

异步非阻塞

  上面写的异步非阻塞并没实际用途,下面是它的一个应用场景,在代码中,MainHandler的get方法中,fetch()比较耗时,但其返回一Future对象,当我们先访问‘/mani’时,MainHandler中get方法会阻塞在这里,但当我们此时去访问访问‘/page’,PageHandler中的get方法会立即执行

  1. #coding:utf-8
  2.  
  3. import tornado.web
  4. import tornado.ioloop
  5. from tornado import gen, httpclient
  6. from tornado.concurrent import Future
  7.  
  8. class MainHandler(tornado.web.RequestHandler):
  9.  
  10. @gen.coroutine
  11. def get(self):
  12. http = httpclient.AsyncHTTPClient() #发送异步请求
  13. data = yield http.fetch('https://www.youtube.com/',raise_error=False) #其源码中可以看到return future,即返回future对象
  14. print 'done',data
  15. self.write('main')
  16. self.finish('dd')
  17.  
  18. # 加入回调函数处理
  19. # @gen.coroutine
  20. # def get(self):
  21. # http = httpclient.AsyncHTTPClient() #发送异步请求
  22. # yield http.fetch('https://www.youtube.com/',callback=self.done,raise_error=False) #其源码中可以看到return future,即返回future对象
  23. #
  24. # def done(self,response):
  25. # print 'done',response
  26. # self.write('main')
  27. # self.finish('dd')
  28.  
  29. class PageHandler(tornado.web.RequestHandler):
  30.  
  31. def get(self):
  32. self.write('page')
  33.  
  34. application = tornado.web.Application([
  35. (r'/main',MainHandler),
  36. (r'/page',PageHandler)
  37. ])
  38.  
  39. if __name__ == '__main__':
  40. application.listen(8888)
  41. tornado.ioloop.IOLoop.instance().start()

  从python 3.5 开始,关键字async 和 await可以用来代替@gen.coroutine +yield,代码如下:

http://www.tornadoweb.org/en/stable/guide/coroutines.html

  1. async def fetch_coroutine(url):
  2. http_client = AsyncHTTPClient()
  3. response = await http_client.fetch(url)
  4. return response.body
  5.  
  6. '''
  7. # Decorated: # Native:
  8.  
  9. # Normal function declaration
  10. # with decorator # "async def" keywords
  11. @gen.coroutine
  12. def a(): async def a():
  13. # "yield" all async funcs # "await" all async funcs
  14. b = yield c() b = await c()
  15. # "return" and "yield"
  16. # cannot be mixed in
  17. # Python 2, so raise a
  18. # special exception. # Return normally
  19. raise gen.Return(b) return b
  20. '''

  其实现异步阻塞的关键在于Future对象,下面是其部分源码,可以看到其_result属性初始化没有值,tornado内部会监听每一个Future对象的_result属性值,若没有值时,继续阻塞,若有值时,若某个Future对象的_result属性值有值了,处理该请求,结束阻塞,继续监听其他Future对象。

关于Future类可以参考:https://www.cnblogs.com/silence-cho/p/9867499.html

  1. class Future(object):
  2. """Represents the result of an asynchronous computation."""
  3.  
  4. def __init__(self):
  5. """Initializes the future. Should not be called by clients."""
  6. self._condition = threading.Condition()
  7. self._state = PENDING
  8. self._result = None
  9. self._exception = None
  10. self._traceback = None
  11. self._waiters = []
  12. 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

tornado框架学习的更多相关文章

  1. tornado框架学习及借用有道翻译api做自动翻译页面

    趁着这几天有时间,就简单的学了一下tornado框架,简单做了个自动翻译的页面 仅为自己学习参考,不作其他用途 文件夹目录结构如下: . ├── server.py ├── static │   └─ ...

  2. 小白学习tornado框架第一站-环境设置

    首先建立一个虚拟环境 mkvirtualenv -p /usr/bin/python3 tornado_1 安装tornado框架 pip install tornado  pycham中建立同步 创 ...

  3. tornado框架基础10-websocket

    websocket 01 长轮询 在网页,我们经常扫码登录,结合之前的学习的知识点,来思考下,前端是如何知道用户在手机上扫码登录了呢? 长轮询:客户端不断的向服务器发送请求 缺点: \1. 开销大 \ ...

  4. tornado框架基础01-路由简介

    tornado 小而精 Django 大而全 Web框架 Tornado是一个由Python开发的Web框架 Web服务 利用Tornado,可以快速搭建和一个高性能的Web服务 非阻塞 Tornad ...

  5. Beego框架学习--(核心:数据交互)

    Beego框架学习记录 1.beego简介 beego 是一个快速开发 Go 应用的 HTTP 框架,他可以用来快速开发 API.Web 及后端服务等各种应用,是一个 RESTful 的框架,主要设计 ...

  6. 说什么也要脱单——Python WEB开发:用Tornado框架制作简易【表白墙】网站

    先来哔哔两句:(https://jq.qq.com/?_wv=1027&k=QgGWqAVF) 今天我们要用Python做Web开发,做一个简单的[表白墙]网站.众所周知表白墙的功能普遍更多的 ...

  7. IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习保护API

    IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习之保护API. 使用IdentityServer4 来实现使用客户端凭据保护ASP.N ...

  8. Hadoop学习笔记—18.Sqoop框架学习

    一.Sqoop基础:连接关系型数据库与Hadoop的桥梁 1.1 Sqoop的基本概念 Hadoop正成为企业用于大数据分析的最热门选择,但想将你的数据移植过去并不容易.Apache Sqoop正在加 ...

  9. Spring框架学习一

    Spring框架学习,转自http://blog.csdn.net/lishuangzhe7047/article/details/20740209 Spring框架学习(一) 1.什么是Spring ...

随机推荐

  1. Spring @Scheduled执行原理解析

    项目使用很多@Scheduled(cron=**) 注解来实现定时任务,既然要用就必须弄清楚的它的实现原理,于是乎翻了一下相关的源码. Spring 3.0之后增加了调度器功能,提供的@Schedul ...

  2. 阻塞IO和非阻塞IO

    1 TCP协议 每一个TCP通信的的socket的内核里面都会有一个发送缓冲区和接收缓冲区 发送端 : send 报文 -- TCP发送缓冲区 -- 接收端 :TCP接收缓冲区 -- receive ...

  3. element 文件上传大小控制

    1.页面代码 <el-upload :show-file-list="false" class="upload-demo" :before-upload= ...

  4. 【python+selenium】截取某个元素

    一. selenium截图1.selenium提供了几个截取全屏的方法- get_screenshot_as_file(self, filename) --这个方法是获取当前window的截图,出现I ...

  5. 初级文件IO——若干种文件共享操作 如何影响 文件文件描述符表

    同一进程共享操作相同的文件 在同一个进程中多次open打开同一文件时,文件描述符可能会相同吗? 答:不可能.在同一进程里面,一旦某个文件描述符被用了,在close释放之前,别人不可能使用,所以指向同一 ...

  6. xcode 中 vary for traits详解

    https://www.jianshu.com/p/d6896437e5a7  这篇文章写的很好!

  7. jQuery获取上传文件的名称

    //获取文件名称 function getFileName(path) {     var pos1 = path.lastIndexOf('/');     var pos2 = path.last ...

  8. 冒泡排序Bubble_Sort

    基本原理:对于冒泡排序来说,基本思想是从第一个元素开始,数组中的数据依次和它后面相邻的数据进行比较,即1和2比较,2和3比较,a和a+1比较,直到倒数第二位和倒数第一位的比较,如果顺序不对就进行交换, ...

  9. 通过LVM备份mysql数据库脚本

    #!/bin/bash #******************************************************************** #encoding -*-utf8- ...

  10. dockerfile-maven plugin自动镜像制作并发布

    环境准备:win10+docker 1.打开hyper-v 2.下载最新版本docker:https://store.docker.com/editions/community/docker-ce-d ...