python开发之Tornado
一、Tornado基础
Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。我们开发这个 Web 服务器的主要目的就是为了处理 FriendFeed 的实时功能 ——在 FriendFeed 的应用里每一个活动用户都会保持着一个服务器连接。
1. 基本步骤
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()
- 第一步:执行脚本,监听 8888 端口。
- 第二步:浏览器客户端访问 /index --> http://127.0.0.1:8888/index。
- 第三步:服务器接受请求,并交由对应的类处理该请求。
- 第四步:类接受到请求之后,根据请求方式(post / get / delete …)的不同调用并执行相应的方法。
- 第五步:方法返回值的字符串内容发送浏览器。
2. 路由系统
路由系统其实就是 url 和 类 的对应关系,这里不同于其他框架,其他很多框架均是 url 对应 函数,Tornado中每个url对应的是一个类。
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('buy.wupeiqi.com$', [
(r'/index',BuyHandler),
])
if __name__ == "__main__":
application.listen(80)
tornado.ioloop.IOLoop.instance().start()
3. 模板
- Tornao中的模板语言和django中类似,模板引擎将模板文件载入内存,然后将数据嵌入其中,最终获取到一个完整的字符串,再将字符串返回给请求者。
- Tornado 的模板支持“控制语句”和“表达语句”,控制语句是使用 {% 和 %} 包起来的 例如 {% if len(items) >2 %}。表达语句是使用 { { 和 }} 包起来的,例如 { { items[0] }}。
- 控制语句和对应的 Python 语句的格式基本完全相同。我们支持 if、for、while 和 try,这些语句逻辑结束的位置需要用 {% end %} 做标记。还通过 extends 和 block 语句实现了模板继承。这些在 template 模块的代码文档中有着详细的描述。
layout.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<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>
index.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 %}
app.py
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('home/index.html')
settings = {
'template_path': 'template',
}
application = tornado.web.Application([
(r"/index", MainHandler),
], **settings)
if __name__ == "__main__":
application.listen(80)
tornado.ioloop.IOLoop.instance().start()
在模板中默认提供了一些函数、字段、类以供模板使用:
escape: tornado.escape.xhtml_escape 的別名
xhtml_escape: tornado.escape.xhtml_escape 的別名
url_escape: tornado.escape.url_escape 的別名
json_encode: tornado.escape.json_encode 的別名
squeeze: tornado.escape.squeeze 的別名
linkify: tornado.escape.linkify 的別名
datetime: Python 的 datetime 模组
handler: 当前的 RequestHandler 对象
request: handler.request 的別名
current_user: handler.current_user 的別名
locale: handler.locale 的別名
_: handler.locale.translate 的別名
static_url: for handler.static_url 的別名
xsrf_form_html: handler.xsrf_form_html 的別名
Tornado默认提供的这些功能其实本质上就是 UIMethod 和 UIModule,我们也可以自定义从而实现类似于Django的simple_tag的功能:
1、定义
# uimethods.py
def tab(self):
return 'UIMethod'
# uimodules.py
from tornado.web import UIModule
from tornado import escape
class custom(UIModule):
def render(self, *args, **kwargs):
return escape.xhtml_escape('<h1>wupeiqi</h1>')
#return escape.xhtml_escape('<h1>wupeiqi</h1>')
2、注册
import tornado.ioloop
import tornado.web
from tornado.escape import linkify
import uimodules as md
import uimethods as mt
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
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()
3、使用
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<link href="{
{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body>
<h1>hello</h1>
{
% module custom(123) %}
{
{
tab() }}
</body>
UIMethod & UIModule
- UIMethods 认为所有返回的都是非法的,所以在页面显示的是经过转义之后的,比如浏览器页面显示成这样:<h3>老村长</h3>,这个转义是tornado内部做的。
- 两个办法告诉tornado不要自动帮我转义了:(1)在settings里写"autoescape" : None, (2)在前端写成 {% raw tab(123) %} 。
- UIModules 不会自动帮我们转义,如果我们想转义,就手动导入from tornado import
escape,在return里写成 return escape.xhtml_escape(’<h3>老村长</h3>’),转义会转义成 <h3>老村长</h3> 在浏览器页面显示为 <h3>老村长</h3>。 - UIMethods只能返回指定内容,UIModules即可以返回指定内容,还可以生成css和js。
Tornado & Django
- tornado的路由严格遵循设置,设置为(r"^/login", LoginHandler),在浏览器输入网址就是http://127.0.0.1:8000/login ;如果设置为(r"^/login/", LoginHandler),在浏览器就要输入http://127.0.0.1:8000/login/。而对于Django的路由,如果在设置时最后没加/,在浏览器输入时最后加了/,也可以成功。
- Chrome浏览器会在你输入的网址后面自动补上/,所以如果tornado的路由设置为(r"^/login", LoginHandler),则会报错:WARNING:tornado.access:404 GET /login/ (127.0.0.1) 1.00ms ;而火狐浏览器不会自动在你输入的网址后面补/,所以能够成功执行。Microsoft Edge也会自动在你输入的网址后面补/ 。如果tornado的路由设置为(r"^/login.html", LoginHandler), 就不会报错。
二、利用Tornado自定义session组件
1. 知识预备
class C:
def f1(self):
print('C')
# super(C, self).f1() # super按照查找顺序执行,C没有父类,深度优先查完变广度优先,找到B类的f1方法
class A(C):
def f1(self):
print('A')
# super(A,self).f1()
B.f1(self) # 想在A的f1中直接调用B的f1
class B:
def f1(self):
print('B')
class Foo(A,B):
pass
obj = Foo()
obj.f1()
两种调用f1的方法: