一、概述
Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本。这个 Web 框架看起来有些像是一个py文件,不过为了能有效利用非阻塞式服务器环境,这个 Web 框架还包含了一些相关的有用工具和优化。
Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。
首先将带着大家一起来剖析基于python编写的Web框架 tornado ,本着易读易懂的目标来写这一系列,寄希让小白也能明白其中的道理。
在了解Tornado之前,让我们来看一下Tornado框架背后的原理,其实就是运用了socket。
运行start.py脚本并在浏览器上访问http://127.0.0.1:8080 , 你会看到页面输出 Hello, Seven
#!/usr/bin/env python #coding:utf-8 import socket def handle_request(client): buf = client.recv(1024) client.send("HTTP/1.1 200 OK ") client.send("Hello, Seven") def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost',8080)) sock.listen(5) while True: connection, address = sock.accept() handle_request(connection) connection.close() if __name__ == '__main__': main()
分析:
① 浏览器其实就是一个socket客户端,而web应用其实就是一个socket服务端,并且web应用在服务器上一直在监听某个端口。
② 当浏览器请求某个web应用时,需要指定服务器的IP(DNS解析)和端口建立一个socket连接。
③ 建立链接后,web应用根据请求的不同,给用户返回相应的数据。
④ 断开socket连接。(之所以说http是短链接,其实就是因为每次请求完成后,服务器就会断开socket链接)
对于Web框架来说,一般分为两类:
① 其中一类则是包含上述 4部分 内容的框架,② 另外一类就是只包含第3部分功能的框架。
Tornado就是一中属于前者的框架。Tornado 是一个基于 Python 开发的web框架,较其他 Web 框架的区别是:采用了非阻塞的方式和对epoll的应用。这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。
二、Tornado的简单应用
下载安装:
pip3 install tornado 源码安装: https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz
简单的hello world 案例,在浏览器输入http://127.0.0.1:8888/index :
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") application = tornado.web.Application([ (r"/index", MainHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
运行该脚本,依次执行:
① 创建一个Application对象,并把一个正则表达式'/'和类名MainHandler传入构造函数:tornado.web.Application(...)
② 执行Application对象的listen(...)方法,即:application.listen(8888)
③ 执行IOLoop类的类的 start() 方法,即:tornado.ioloop.IOLoop.instance().start()
④ 整个过程其实就是在创建一个socket服务端并监听8888端口,当请求到来时,根据请求中的url和请求方式(post、get或put等)来指定相应的类中的方法来处理本次请求
ps:
#!/usr/bin/env python # -*- coding:utf-8 -*- #!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web from tornado import httpclient from tornado.web import asynchronous from tornado import gen import uimodules as md import uimethods as mt class MainHandler(tornado.web.RequestHandler): @asynchronous @gen.coroutine def get(self): print 'start get ' http = httpclient.AsyncHTTPClient() http.fetch("http://127.0.0.1:8008/post/", self.callback) self.write('end') def callback(self, response): print response.body settings = { 'template_path': 'template', # 前端页面 'static_path': 'static', 'static_url_prefix': '/static/', 'ui_methods': mt, # 自定制模板 'ui_modules': md, # 自定制模板 } application = tornado.web.Application([ (r"/index", MainHandler), ], **settings) if __name__ == "__main__": application.listen(8009) tornado.ioloop.IOLoop.instance().start()
三、流程介绍
(1)路由系统
路由系统其实就是 url 和 类 的对应关系,这里不同于其他框架,其他很多框架均是 url 对应 函数,Tornado中每个url对应的是一个类。
#!/usr/bin/env python # -*- coding:utf-8 -*- 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 BuyHandler(tornado.web.RequestHandler): def get(self): self.write("buy.wupeiqi.com/index") application = tornado.web.Application([ (r"/index", MainHandler), (r"/story/([0-9]+)", StoryHandler), ]) application.add_handlers('ray.com$', [ (r'/index',BuyHandler), # 二级 ]) if __name__ == "__main__": application.listen(80) tornado.ioloop.IOLoop.instance().start()
(2)模板引擎
1) 默认
Tornao中的模板语言和django中类似,模板引擎将模板文件载入内存,然后将数据嵌入其中,最终获取到一个完整的字符串,再将字符串返回给请求者。
Tornado 的模板支持“控制语句”和“表达语句”,控制语句是使用 {% 和 %} 包起来的 例如 {% for item in demo %}。表达语句是使用 {{ }} 包起来的,例如 {{ items[0] }}。
通过 extends 和 block 语句实现了模板继承。
注意:
在使用模板前需要在setting中设置模板路径:"template_path" : "views" --> (别名不规定)
这里跟Django有点不太一样,Django获取是直接用 点 来获取值的,Tornado要使用列表形式获取,如{{ items[0] }}
控制语句要用{% end %} 结束
2) 自定义UIMethod和UIModule
import tornado.ioloop import tornado.web from controllers.account import LoginHandler from controllers.home import HomeHandler class MainHandler(tornado.web.RequestHandler): def get(self): # self.write("Hello, world") self.render("main.html") # self.redirect('http://www.baidu.com') import uimethods as ut import uimodules as mm settings = { "template_path": 'views', # 模板 "cookie_secret":'abcdefgijk', # cookie加密 "ui_methods":ut, # 模板标签不渲染 "ui_modules":mm, # 模板标签自动渲染 "static_path":'static', } application = tornado.web.Application([ (r"/index", MainHandler), (r"/login", LoginHandler), (r"/home", HomeHandler), ],**settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
""" 模板标签不自动渲染 """ from tornado import escape def tab(request,val): # print(request) # LoginHandler # print(val) # 前端传过来的值 return "<h1>这是模板</h1>"
""" 模板自动帮我们渲染 """ from tornado.web import UIModule from tornado import escape class Foo(UIModule): def css_files(self): """添加static下面的common.css样式到login.html""" return "common.css" def embedded_css(self): """添加下面的样式到login.html""" return ".c1{display:none}" def javascript_files(self): """添加static下面的common.js样式到login.html""" return "common.js" def embedded_javascript(self): """添加下面的JS样式到login.html""" return "function fun(){alert(1)}" def render(self): return "<h1>这是modules模板</h1>" # return escape.xhtml_escape("<h1>这是modules模板</h1>")
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> </head> <body> {{ tab(666) }} <!--不渲染--> {% raw tab(11) %} <!--帮我们渲染--> {% module Foo() %} <!--自动渲染--> <form action="/login" method="post"> <input type="text" name="username"><br> <input type="text" name="password"><br> <input type="submit" value="提交">{{ msg }} </form> <script> window.onload=function () { // fun(); } </script> </body> </html>
参考: