? ?tornado框架是一款相較于其他web framework處理服務器性能問題更加強健的輕量級的強大的Python web框架。tornado是一個可定制的非阻塞式異步加載框架,速度相當快。
大綱:
1,tornado入門
2,tornado表單和模板
3,模板擴展
4,數據庫
5,異步web服務
6,編寫安全應用
7,外部服務認證
8,部署tornado
tornado安裝
? ? ?tornado的官網:http://www.tornadoweb.org/
? ? ? ? 1,直接下載安裝包并解壓安裝
? ? ? ? ?2,使用pip安裝—>sudo pip install tornado
tornado簡單的web服務
? ??Tornado是一個編寫對HTTP請求響應的框架。下面是一個簡單的Tornado應用的基礎示例:
#引入需要的模塊和包
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options
#定義段變量,這里默認為8000,可自己設置
define("port", default=8000, help="run on the given port", type=int)
#聲明一個主頁處理類,繼承自RequestHandler
class IndexHandler(tornado.web.RequestHandler):
#重寫父類的get方法,當請求為get方式時,執行此方法
def get(self):
greeting = self.get_argument('greeting', 'Hello')
#用write方法將需要的內容顯示到網頁上
self.write(greeting + ', friendly user!')
#程序運行的入口
if __name__ == "__main__":
#得到所有的輸入參數和參數值
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
#創建一個HTTP對象
http_server = tornado.httpserver.HTTPServer(app)
#監聽接口
http_server.listen(options.port)
#創建一個I/O實例并啟動
tornado.ioloop.IOLoop.instance().start()
參數handler
? ??它是一個元組組成的列表,其中每個元組的第一個元素是一個用于匹配的正則表達式,第二個元素是一個RequestHanlder類。在hello.py中,我們只指定了一個正則表達式-RequestHanlder對,但你可以按你的需要指定任意多個。
表單和模板
Tornado自身提供了一個輕量級、快速并且靈活的模板語言在tornado.template模塊中。
接下來是實例(寫了比較重要的地方,其他的地方略去)
if __name__ == '__main__':
tornado.options.parse_command_line()
app = tornado.web.Application(
#路由映射
handlers=[(r'/', IndexHandler), (r'/poem', PoemPageHandler)],
#這里需要導入os模塊配置模板路徑,否則代碼會報錯
template_path=os.path.join(os.path.dirname(__file__), "templates")
)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
此時需要創建一個模板文件夾,然后在此文件夾下創建需要的HTML文件就可以加載到程序中。當然需要在后臺調用渲染才可以出效果
模板語法:
? ? ? ? 先給大家介紹一種簡單的應用語法-->{{花括號里放需要渲染的變量或對象屬性}}
tornado會通過Python解釋器將模板語法解釋出來
填充表達式:
控制流語句:
if選擇結構:
{%if 條件%}
{%endif%}
for循環結構:
{%for i in 變量%}
{%endfor%}
模板函數:
Tornado在所有模板中默認提供了一些簡單便利的函數。它們包括:
escape(s)
替換字符串s中的&、<、>為他們對應的HTML字符。
url_escape(s)
使用urllib.quote_plus替換字符串s中的字符為URL編碼形式。
json_encode(val)
將val編碼成JSON格式。(在系統底層,這是一個對json庫的dumps函數的調用。查閱相關的文檔以獲得更多關于該函數接收和返回參數的信息。)
squeeze(s)
過濾字符串s,把連續的多個空白字符替換成一個空格。
設置靜態文件
第一步先配置路徑:
app = tornado.web.Application(
handlers=[(r'/', IndexHandler), (r'/poem', MungedPageHandler)],
template_path=os.path.join(os.path.dirname(__file__), "templates"),
#配置靜態文件路徑
static_path=os.path.join(os.path.dirname(__file__), "static"),
debug=True
)
static的子目錄作為static_path的參數。現在應用將以讀取static目錄下的filename.ext來響應諸如/static/filename.ext的請求,并在響應的主體中返回。
static_url可以逆向生成文件的路徑,如:
<link rel="stylesheet" href="static_url('style.css')">
這個對static_url的調用生成了URL的值,并渲染輸出類似下面的代碼:
<link rel="stylesheet" href="/static/style.css">
模板擴展
模板的繼承與替換?
意義:簡單,便捷,大大增加了代碼的復用性,增加了代碼的可讀性
Tornado通過extends和block語句支持模板繼承{% extends "filename.html" %}。
比如,為了在新模板中擴展一個父模板(在這里假設為main.html),你可以這樣使用:
{% extends "main.html" %}
然后,為了在子模板index.html中覆寫{% block header %}{% end %}部分,你可以使用塊的名字引用,并把任何你想要的內容放到其中。
{% block header %}{% end %}
{% block header %}
<**>hello world<**>
{% end %}
任何繼承這個模板的文件都可以包含它自己的{% block header %}和{% end %},然后把一些不同的東西加進去。
UI模塊:
UI模塊是封裝模板中包含的標記、樣式以及行為的可復用組件。它所定義的元素通常用于多個模板交叉復用或在同一個模板中重復使用。模塊本身是一個繼承自Tornado的UIModule類的簡單Python類,并定義了一個render方法。當一個模板使用{% module Foo(...) %}標簽引用一個模塊時,Tornado的模板引擎調用模塊的render方法,然后返回一個字符串來替換模板中的模塊標簽。UI模塊也可以在渲染后的頁面中嵌入自己的JavaScript和CSS文件,或指定額外包含的JavaScript或CSS文件。你可以定義可選的embedded_javascript、embedded_css、javascript_files和css_files方法來實現這一方法
如:app = tornado.web.Application(
handlers=[(r'/', HelloHandler)],
template_path=os.path.join(os.path.dirname(__file__), 'templates'),
ui_modules={'Hello': HelloModule}
)
此時在網頁中寫入{% module Hello() %}就會渲染為HelloModule
數據庫:
torndb是一個輕量級的基于MySQLdb封裝的一個模塊,從tornado3.0版本以后,其已經作為一個獨立模塊發行了。torndb依賴于MySQLdb模塊,因此,在使用torndb模塊時,要保證系統中已經有MySQLdb模塊。
torndb模塊僅提供了數據庫的連接和表項操作指令,沒有提供數據庫的創建等操作命令。所以需要自己定義數據庫操作的增刪改查
(1)連接數據庫
import torndb
db=torndb.Connection(hostaddress,database name,user,password)
(2)以get方式查詢一行數據
>>> a=db.get('select * from query where id=1')
>>> a
{'queryc': 'dac', 'id': 1}
(3)以query方式查詢一列數據
>>> a=db.query('select * from query')
>>> a
[{'id': 2, 'queryc': 'isca'}, {'id': 1, 'queryc': 'dac'}]
(4)執行無返回參數的命令
string='dac'
str='insert into query(id,queryc)values(%d,"%s")'%(1,string)
db.execute(exe)
另外,torndb還提供了插入、插入多行數據的函數,實際這些函數并不必要,直接調用無返回參數的命令即可完成。
而且,torndb模塊并并沒有給MySQLdb模塊增加額外功能,建議在python中使用mysql數據庫時,直接使用MySQLdb模塊即可。
異步web服務
大部分Web應用(包括我們之前的例子)都是阻塞性質的,Tornado給了我們更好的方法來處理這種情況。應用程序在等待第一個處理完成的過程中,讓I/O循環打開以便服務于其他客戶端,直到處理完成時啟動一個請求并給予反饋,而不再是等待請求完成的過程中掛起進程Tornado包含一個AsyncHTTPClient類,可以執行異步HTTP請求。
例:
from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoopimport tornado.web
from tornado.httpclient import AsyncHTTPClient
import json
class IndexHandler(RequestHandler):
?# 通過裝飾器注解的方式,告訴tornado,這個處理方式是異步操作~開發人員要通過代碼手工控制返回響應?
?@tornado.web.asynchronous def get(self):?
?# 創建一個異步請求對象?
?client = AsyncHTTPClient()
?# 異步請求對象發送請求,獲取數據?
?client.fetch("https://suggest.taobao.com/sug?code=utf-8&q=iphoneX",\ callback=self.deal_response)?
?def deal_response(self, response):?
?content = response.body?
?json_ = json.loads(content)?
?print(json_, type(json_))?
?self.write("
異步ok!
")? ?
?self.finish()
if __name__ == "__main__":??
? app = Application([(r"/", IndexHandler), ])?
?? app.listen(8000)?
?? IOLoop.current().start()
異步裝飾器和finish方法:
Tornado默認在函數處理返回時關閉客戶端的連接。在通常情況下,這正是你想要的。但是當我們處理一個需要回調函數的異步請求時,我們需要連接保持開啟狀態直到回調函數執行完畢。你可以在你想改變其行為的方法上面使用@tornado.web.asynchronous裝飾器來告訴Tornado保持連接開啟,記住當你使用@tornado.web.asynchonous裝飾器時,Tornado永遠不會自己關閉連接。你必須在你的RequestHandler對象中調用finish方法來顯式地告訴Tornado關閉連接。(否則,請求將可能掛起,瀏覽器可能不會顯示我們已經發送給客戶端的數據。)我們在函數的write后面調用了finish方法表示完成
tornado長輪詢:
我自己的理解:長輪詢就是客戶端發送一個ajax請求,如果客戶端未更新數據,一直等待,若更新數據則返回響應,然后再發一個請求等待。
所謂的"服務器推送"技術允許Web應用實時發布更新,同時保持合理的資源使用以及確保可預知的擴展。對于一個可行的服務器推送技術而言,它必須在現有的瀏覽器上表現良好。最流行的技術是讓瀏覽器發起連接來模擬服務器推送更新。這種方式的HTTP連接被稱為長輪詢或Comet請求。
長輪詢意味著瀏覽器只需啟動一個HTTP請求,其連接的服務器會有意保持開啟。瀏覽器只需要等待更新可用時服務器"推送"響應。當服務器發送響應并關閉連接后,(或者瀏覽器端客戶請求超時),客戶端只需打開一個新的連接并等待下一個更新
tornado與websocket:
Tornado在websocket模塊中提供了一個WebSocketHandler類。這個類提供了和已連接的客戶端通信的WebSocket事件和方法的鉤子。當一個新的WebSocket連接打開時,open方法被調用,而on_message和on_close方法分別在連接接收到新的消息和客戶端關閉時被調用。
此外,WebSocketHandler類還提供了write_message方法用于向客戶端發送消息,close方法用于關閉連接。
class EchoHandler(tornado.websocket.WebSocketHandler):
def open(self):
self.write_message('connected!')
def on_message(self, message):
self.write_message(message)
正如你在我們的EchoHandler實現中所看到的,open方法只是使用WebSocketHandler基類提供的write_message方法向客戶端發送字符串"connected!"。每次處理程序從客戶端接收到一個新的消息時調用on_message方法,我們的實現中將客戶端提供的消息原樣返回給客戶端。
編寫安全:
很多時候,安全應用是以犧牲復雜度(以及開發者的頭痛)為代價的。Tornado Web服務器從設計之初就在安全方面有了很多考慮,使其能夠更容易地防范那些常見的漏洞。安全cookies防止用戶的本地狀態被其瀏覽器中的惡意代碼暗中修改。此外,瀏覽器cookies可以與HTTP請求參數值作比較來防范跨站請求偽造攻擊。在本章中,我們將看到使防范這些漏洞更簡單的Tornado功能,以及使用這些功能的一個用戶驗證示例。
6.1 Cookie漏洞?
許多網站使用瀏覽器cookies來存儲瀏覽器會話間的用戶標識。這是一個簡單而又被廣泛兼容的方式來存儲跨瀏覽器會話的持久狀態。不幸的是,瀏覽器cookies容易受到一些常見的攻擊。本節將展示Tornado是如何防止一個惡意腳本來篡改你應用存儲的cookies的。
6.1.1 Cookie偽造?
有很多方式可以在瀏覽器中截獲cookies。JavaScript和Flash對于它們所執行的頁面的域有讀寫cookies的權限。瀏覽器插件也可由編程方法訪問這些數據。跨站腳本攻擊可以利用這些訪問來修改訪客瀏覽器中cookies的值。
6.1.2 安全Cookies?
Tornado的安全cookies使用加密簽名來驗證cookies的值沒有被服務器軟件以外的任何人修改過。因為一個惡意腳本并不知道安全密鑰,所以它不能在應用不知情時修改cookies。
6.1.2.1 使用安全Cookies?
Tornado的set_secure_cookie()和get_secure_cookie()函數發送和取得瀏覽器的cookies,以防范瀏覽器中的惡意修改。為了使用這些函數,你必須在應用的構造函數中指定cookie_secret參數