Tornado 是 FriendFeed 使用的可擴充的非阻塞式 web 伺服器及其相關工具的開源版本。這個 Web 架構看起來有些像web.py 或者 Google 的 webapp,不過為了能有效利用非阻塞式伺服器環境,這個 Web 架構還包含了一些相關的有用工具 和優化。
Tornado 和現在的主流 Web 伺服器架構(包括大多數 Python 的架構)有着明顯的差別:它是非阻塞式伺服器,而且速度相當快。得利于其 非阻塞的方式和對 epoll 的運用,Tornado 每秒可以處理數以千計的連接配接,這意味着對于實時 Web 服務來說,Tornado 是一個理想的 Web 架構。我們開發這個 Web 伺服器的主要目的就是為了處理 FriendFeed 的實時功能 ——在 FriendFeed 的應用裡每一個活動使用者都會保持着一個伺服器連接配接。
安裝:
pip install tornado
源碼安裝
https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz
基本使用
# -*- coding:utf-8 -*-
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 ...)的不同調用并執行相應的方法
第五步:方法傳回值的字元串内容發送浏覽器
# -*- 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()
二、路由系統
路由系統其實就是 url 和 類 的對應關系,這裡不同于其他架構,其他很多架構均是 url 對應 函數,Tornado中每個url對應的是一個類。
# -*- 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('buy.wupeiqi.com$', [
(r'/index',BuyHandler),
])
if __name__ == "__main__":
application.listen(80)
tornado.ioloop.IOLoop.instance().start()
三、模闆
Tornao中的模闆語言和django中類似,模闆引擎将模闆檔案載入記憶體,然後将資料嵌入其中,最終擷取到一個完整的字元串,再将字元串傳回給請求者。
Tornado 的模闆支援“控制語句”和“表達語句”,控制語句是使用 {% 和 %} 包起來的 例如 {% if len(items) > 2 %}。表達語句是使用 {{ 和 }} 包起來的,例如 {{ items[0] }}。
控制語句和對應的 Python 語句的格式基本完全相同。我們支援 if、for、while 和 try,這些語句邏輯結束的位置需要用 {% end %} 做标記。還通過 extends 和 block 語句實作了模闆繼承。
<!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>
{% 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 %}
# -*- coding:utf-8 -*-
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 的別名
四、實用功能
1、靜态檔案
對于靜态檔案,可以配置靜态檔案的目錄和前段使用時的字首,并且Tornaodo還支援靜态檔案緩存。
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('home/index.html')
settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
}
application = tornado.web.Application([
(r"/index", MainHandler),
], **settings)
if __name__ == "__main__":
application.listen(80)
tornado.ioloop.IOLoop.instance().start()
<!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>
</body>
</html>
2、csrf
Tornado中的誇張請求僞造和Django中的相似,跨站僞造請求(Cross-site request forgery)
settings = {
"xsrf_cookies": True,
}
application = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
], **settings)
<form action="/new_message" method="post">
{{ xsrf_form_html() }}
<input type="text" name="message"/>
<input type="submit" value="Post"/>
</form>
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使用時,本質上就是去擷取本地的cookie,攜帶cookie再來發送請求
3、cookie
Tornado中可以對cookie進行操作,并且還可以對cookie進行簽名以放置僞造。