Tornado 4.3
於2015年11月6日發佈,該版本正式支持Python3.5
的async
/await
關鍵字,而且用舊版本CPython編譯Tornado一樣可使用這兩個關鍵字,這無疑是一種進步。其次,這是最後一個支持Python2.6
和Python3.2
的版本了,在後續的版本了會移除對它們的兼容。如今網絡上尚未Tornado4.3
的中文文檔,因此爲了讓更多的朋友能接觸並學習到它,我開始了這個翻譯項目,但願感興趣的小夥伴能夠一塊兒參與翻譯,項目地址是tornado-zh on Github,翻譯好的文檔在Read the Docs上直接能夠看到。歡迎Issues or PR。git
實時web功能須要爲每一個用戶提供一個多數時間被閒置的長鏈接,在傳統的同步web服務器中,這意味着要爲每一個用戶提供一個線程,固然每一個線程的開銷都是很昂貴的.github
爲了儘可能減小併發鏈接形成的開銷,Tornado使用了一種單線程事件循環的方式.這就意味着全部的應用代碼都應該是異步非阻塞的,由於在同一時間只有一個操做是有效的.web
異步和非阻塞是很是相關的而且這兩個術語常常交換使用,但它們不是徹底相同的事情.服務器
一個函數在等待某些事情的返回值的時候會被 阻塞. 函數被阻塞的緣由有不少:網絡I/O,磁盤I/O,互斥鎖等.事實上 每一個 函數在運行和使用CPU的時候都或多或少會被阻塞(舉個極端的例子來講明爲何對待CPU阻塞要和對待通常阻塞同樣的嚴肅: 好比密碼哈希函數bcrypt, 須要消耗幾百毫秒的CPU時間,這已經遠遠超過了通常的網絡或者磁盤請求時間了).網絡
一個函數能夠在某些方面阻塞在另一些方面不阻塞.例如, tornado.httpclient
在默認的配置下,會在DNS解析上面阻塞,可是在其餘網絡請求的時候不阻塞(爲了減輕這種影響,能夠用 ThreadedResolver
或者是經過正確配置 libcurl
用 tornado.curl_httpclient
來作).在Tornado的上下文中,咱們通常討論網絡I/O上下文的阻塞,儘管各類阻塞已經被最小化了.併發
異步 函數在會在完成以前返回,在應用中觸發下一個動做以前一般會在後臺執行一些工做(和正常的 同步 函數在返回前就執行完全部的事情不一樣).這裏列舉了幾種風格的異步接口:curl
回調參數異步
返回一個佔位符 (.Future
, Promise
, Deferred
)async
傳送給一個隊列函數
回調註冊表 (POSIX信號)
不論使用哪一種類型的接口, 按照定義 異步函數與它們的調用者都有着不一樣的交互方式;也沒有什麼對調用者透明的方式使得同步函數異步(相似 gevent使用輕量級線程的系統性能雖然堪比異步系統,但它們並無真正的讓事情異步).
一個簡單的同步函數:
from tornado.httpclient import HTTPClient def synchronous_fetch(url): http_client = HTTPClient() response = http_client.fetch(url) return response.body
把上面的例子用回調參數重寫的異步函數:
from tornado.httpclient import AsyncHTTPClient def asynchronous_fetch(url, callback): http_client = AsyncHTTPClient() def handle_response(response): callback(response.body) http_client.fetch(url, callback=handle_response)
使用 Future
代替回調:
from tornado.concurrent import Future def async_fetch_future(url): http_client = AsyncHTTPClient() my_future = Future() fetch_future = http_client.fetch(url) fetch_future.add_done_callback( lambda f: my_future.set_result(f.result())) return my_future
Future
版本明顯更加複雜,可是 Futures
倒是Tornado中推薦的寫法.由於它有兩個主要的優點.首先是錯誤處理更加一致,由於 Future.result
方法能夠簡單的拋出異常(相較於常見的回調函數接口特別指定錯誤處理),並且 Futures
很適合和協程一塊兒使用.協程會在後面深刻討論.這裏是上面例子的協程版本,和最初的同步版本很像:
from tornado import gen @gen.coroutine def fetch_coroutine(url): http_client = AsyncHTTPClient() response = yield http_client.fetch(url) raise gen.Return(response.body)
raise gen.Return(response.body)
聲明是在Python 2 (and 3.2)下人爲執行的, 由於在其中生成器不容許返回值.爲了克服這個問題,Tornado的協程拋出一種特殊的叫 Return
的異常. 協程捕獲這個異常並把它做爲返回值.在Python 3.3和更高版本,使用 return response.body
有相同的結果.