暸解非同步機制:
Tornado 受歡迎的原因,在於它的設計著眼於處理 C10K 問題。舉例來說,當 Server 端接收到查詢資料庫的 Request,有許多時間是在等待資料庫的查詢結果而呈現 Blocking 的狀態。若我們可以利用這些等待的時間來處理其他的 Request,就可以增加整體的效能。所以同樣的 Request,在使用 Tornado 的非同步機制處理下,系統不會苦苦等到資料庫回應才繼續工作,而是將先前的請求暫存起來,先服務其他的 Request,等到資料庫查詢回應後繼續處理剛剛未完成的工作。
實作 Asynchronous :
Tornado 歷經多次重大更新,初期版本中實作非同步需要撰寫 callback 來達成目的,而這樣的方式也讓人詬病。因為你如果需要比較複雜的邏輯處理,可能需要多次的 callback 也造成閱讀、理解上的困難度。因此 Tornado 開發團隊提出了方便的 @gen.coroutine Decorator,接下來就直接使用它來示範,就不再示範前面版本的寫法。
開始實作,假設我們今天提供一個 RESTful 服務,內容包含擷取解析其他網站資訊,這時候我們就可以使用非同步機制來提升效能,程式內容參考如下:
#!/usr/bin/env python
import os
import tornado.web
import tornado.gen
import tornado.ioloop
import tornado.options
import tornado.httpclient
tornado.options.define("ip", default='1.1.1.x', help="message...")
tornado.options.define("port", default=8888, help="message...")
url='http://....'
class Async(tornado.web.RequestHandler):
# 加入 Coroutine,宣告此函式為 Asynchronous
# 使用時必須搭配 yield,Python3.5 可以使用 async 與 await
@tornado.gen.coroutine
def get(self):
client = tornado.httpclient.AsyncHTTPClient()
response = yield client.fetch(url)
# 加入處理邏輯 ...
self.write("...")
settings= {
"debug": True,
"autoreload": True
}
def main():
tornado.options.parse_command_line()
app = tornado.web.Application(
[
(r'/async', Async)
], **settings
)
app.listen(tornado.options.options.port, tornado.options.options.ip)
tornado.ioloop.IOLoop.current().start()
if __name__ == "__main__":
main()
你可以分別測試一般的與非同步的處理速度與平均回應時間,我是使用 Siege 來測試,使用非同步的機制效能的確提升很多,同樣的時間內可以處理更多 Request。若你還想要再提升效能可以使用 tornado.curl_httpclient.CurlAsyncHTTPClient( ),官方文件中有提到 CurlAsyncHTTPClient 會更快,不過需要另外安裝其他套件:pycurl,我實際測試的結果顯示效能的確比 AsyncHTTPClient 更好。
Environment :
・ Arch Linux
・ Python 2.7
Reference :
・ Tornado