Tornado是FriendFeed使用的可擴展的非阻塞式web框架及其相關工具的開源版本。這個Web框架看起來有些像web.py或者Google的 webapp,不過爲了能有效利用非阻塞式服務器環境,這個Web框架還包含了一些相關的有用工具和優化。
Tornado和如今的主流Web服務器框架(包括大多數Python的框架)有着明顯的區別:它是非阻塞式服務器框架,並且速度至關快。因爲其非阻塞的方式和對epoll的運用,Tornado每秒能夠處理數以千計的鏈接,這意味着對於實時Web服務來講,Tornado是一個理想的Web框架。開發這個Web框架的主要目的是爲了處理FriendFeed的實時功能——在FriendFeed的應用裏每個活動用戶都會保持着一個服務器鏈接(關於如何擴容服務器,以處理數以千計的客戶端的鏈接的問題,請參閱C10K問題),同時Tornado是輕量級的web框架,它主要解決了性能問題,讓開發者能夠靈活的去擴展相關組件。css
C10K問題 基於線程的服務器,如Apache,爲了傳入的鏈接,維護了一個操做系統的線程池。Apache會爲每一個HTTP鏈接分配線程池中的一個線程,若是全部的線程都處於被佔用的狀態而且尚有內存可用時,則生成一個新的線程。儘管不一樣的操做系統會有不一樣的設置,大多數Linux發佈版中都是默認線程堆大小爲8MB。Apache的架構在大負載下變得不可預測,爲每一個打開的鏈接維護一個大的線程池等待數據極易迅速耗光服務器的內存資源。 大多數社交網絡應用都會展現實時更新來提醒新消息、狀態變化以及用戶通知,這就要求客戶端須要保持一個打開的鏈接來等待服務器端的任何響應。這些長鏈接或推送請求使得Apache的最大線程池迅速飽和。一旦線程池的資源耗盡,服務器將不能再響應新的請求。 異步服務器在這一場景中的應用相對較新,但他們正是被設計用來減輕基於線程的服務器的限制的。當負載增長時,諸如Node.js,lighttpd和Tornodo這樣的服務器使用協做的多任務的方式進行優雅的擴展。也就是說,若是當前請求正在等待來自其餘資源的數據(好比數據庫查詢或HTTP請求)時,一個異步服務器能夠明確地控制以掛起請求。異步服務器用來恢復暫停的操做的一個常見模式是當合適的數據準備好時調用回調函數。 |
1 安裝html
pip安裝前端 pip install tornado https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gzjquery python setup.py installweb |
2 Tornado執行流程
ajax
#!/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") application = tornado.web.Application([ (r"/index", MainHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
第1步:執行腳本,監聽8888端口; 第2步:瀏覽器客戶端訪問/index --> http://127.0.0.1:8888/index; 第3步:服務器接受請求,並交由對應的類處理該請求; 第4步:類接收到請求以後,根據請求方式(post/get/delete ...)的不一樣調用並執行相應的方法; 第5步:對應方法返回的字符串內容發送到瀏覽器; |
3 路由系統數據庫
Tornado中,路由系統其實就是url和類的對應關係,而其餘web框架,不少均是url和函數的對應關係。django
#!/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('buy.wupeiqi.com$', [ (r'/index',BuyHandler), ]) if __name__ == "__main__": application.listen(80) tornado.ioloop.IOLoop.instance().start()
4 模板json
Tornado中的模板語言與django相似,模板引擎先將模板文件加載到內存,而後將模板文件與數據進行混合,最終獲取到一個完整的字符串,最後將字符串返回給請求者。
Tornado的模板語言支持"控制語句"和"表達語句",控制語句是使用{% %}包起來的,例如{% if len(items) > 2 %}。表達語句是使用{{ xx }}包起來的,例如{{ items[0] }}。
控制語句和對應的Python語句的格式基本徹底相同。支持if、for、while和try,這些語句邏輯結束的位置須要用 {% end %} 作標記。還經過extends和block語句來實現模板的繼承。這些在template模塊的代碼文檔中有着詳細的描述。
tornado程序主文件
#!/usr/bin/env python # -*- coding:utf-8 -*-
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.render('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(8888) tornado.ioloop.IOLoop.instance().start()
layout.html文件(母板)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title> {% block css%} {% end %} </head>
<body>
<div><h1>TEST</h1></div> {% block htmlbody %}{% 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 htmlbody %} <h1 id="test_id" class="tim">Tornado模板.</h1> {% end %} {% block JavaScript %} {% end %}
模板中for循環的語法
{% 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 %}
在模板中默認提供了一些函數、字段、類以供模板使用: 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'
#uimodule.py
#!/usr/bin/env python # -*- coding:utf-8 -*-
from tornado.web import UIModule from tornado import escape class custom(UIModule): def render(self, *args, **kwargs): return escape.xhtml_escape('<h1>wupeiqi</h1>')
(2)註冊
#!/usr/bin/env python # -*- coding:utf-8 -*-
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>
5 實用功能
(1)靜態文件
#!/usr/bin/env python # -*- coding:utf-8 -*-
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.render('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(8888) tornado.ioloop.IOLoop.instance().start()
對於靜態文件,能夠配置靜態文件的目錄和前段使用時的前綴,而且Tornaodo還支持靜態文件緩存。
<!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>
靜態文件的配置:
靜態文件的配置 <link href="{{static_url('css/index.css')}}" rel="stylesheet" /> 同時這種寫法在Tornado中還有另一個功能,即起到緩存的做用。
緩存做用的解釋: 對於靜態文件/static/commons.js,若是前端使用static_url配置路徑後,則相似於在url的後面加了 一串字符串,即/static/commons.js?v=asdf123,那麼當瀏覽器訪問時會攜帶這個特殊的字符串,若是發現 客戶端緩存的靜態文件沒變,則直接渲染前端頁面就行,沒必要再去服務端從新加載頁面,加快了訪問的速度。 |
Tornado實現靜態文件緩存的代碼
def get_content_version(cls, abspath): """Returns a version string for the resource at the given path. This class method may be overridden by subclasses. The default implementation is a hash of the file's contents. .. versionadded:: 3.1 """ data = cls.get_content(abspath) hasher = hashlib.md5() if isinstance(data, bytes): hasher.update(data) else: for chunk in data: hasher.update(chunk) return hasher.hexdigest()
(2)CSRF
Tornado中的跨站請求僞造和Django中的類似,跨站請求僞造(Cross-site request forgery)。
配置python代碼
settings = { "xsrf_cookies": True, } application = tornado.web.Application([ (r"/", MainHandler), (r"/login", LoginHandler), ], **settings)
form表單提交
<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再來發送請求
參考資料:
http://www.cnblogs.com/wupeiqi/articles/5341480.html