tornado非同步web請求
1.為什麼要使用非同步web服務
使用非同步非阻塞請求,併發處理更高效。
2.同步與非同步請求比較
同步請求時,web伺服器程序是阻塞的,也就是說當一個請求被處理時,伺服器程序會被掛起直至請求完成。
非同步請求時,web伺服器程序在等待請求處理過程中,讓I/O迴圈開啟,以便服務於其他請求,請求處理完成後繼續執行回撥函式或生成器,而不再是等待請求過程中掛起程序。整個過程是非同步的。
3.同步與非同步請求示例
同步請求:
class IndexHandler(tornado.web.RequestHandler): def get(self): client=tornado.httpclient.HTTPClient() response=client.fetch("http://test.com/list") self.write("success")
非同步請求:
class IndexAsyncHandler(tornado.web.RequestHandler): @tornado.web.asynchronous def get(self): client=tornado.httpclient.AsyncHTTPClient() client.fetch("http://test.com/list",callback=self.on_response) def on_response(self,response): self.write("success") self.finish()
路由配置:
(r'/test', test_async.IndexHandler), (r'/testasync', test_async.IndexAsyncHandle)
使用http_load工具(關於http_load的使用參見“http_load使用詳解”)進行壓力測試,結果如下:
同步壓力測試:
[[email protected] http_load-12mar2006]# ./http_load -p 100 -s 60 url 27 fetches, 100 max parallel, 189 bytes, in 60 seconds 7 mean bytes/connection 0.45 fetches/sec, 3.15 bytes/sec msecs/connect: 0.113037 mean, 0.258 max, 0.021 min msecs/first-response: 31186.5 mean, 59721.3 max, 2246.32 min HTTP response codes: code 200 -- 27
非同步壓力測試:
209 fetches, 100 max parallel, 1463 bytes, in 60.0046 seconds 7 mean bytes/connection 3.48306 fetches/sec, 24.3814 bytes/sec msecs/connect: 0.0944641 mean, 0.387 max, 0.021 min msecs/first-response: 20088 mean, 30650 max, 10601.1 min HTTP response codes: code 200 -- 209
對比可以看出,在60s時間內,併發請求數量為100的情況下,
同步請求只有27個請求響應,而非同步請求達到了209個
4.非同步請求使用說明
同步請求在請求完畢後,自動關閉連線。
非同步請求保持連線開啟,需要手動關閉連線。
tornado中使用@tornado.web.asynchronous裝飾器作用是保持連線一直開啟,
回撥函式執行完畢後,呼叫finish方法來主動關閉連線。
5.非同步生成器
上例中,是使用回撥函式來做業務處理及關閉連線的。
回撥函式的缺點是,可能引起回撥深淵,系統將難以維護。如回撥中呼叫回撥。
def get(self): client = AsyncHTTPClient() client.fetch("http://example.com", callback=on_response) def on_response(self, response): client = AsyncHTTPClient() client.fetch("http://another.example.com/", callback=on_response2) def on_response2(self, response): client = AsyncHTTPClient() client.fetch("http://still.another.example.com/", callback=on_response3) def on_response3(self, response): [etc., etc.]
tornado2.1引入了tornado.gen模組,可以更整潔地執行非同步請求。
非同步請求:
class IndexGenHandler(tornado.web.RequestHandler): @tornado.web.asynchronous @tornado.gen.engine def get(self): client=tornado.httpclient.AsyncHTTPClient() response=yield tornado.gen.Task(client.fetch,"http://test.com/list") self.write("success") self.finish()
路由配置:
(r'/testgen', test_async.IndexGenHandler),
非同步壓力測試:
207 fetches, 100 max parallel, 1449 bytes, in 60.0055 seconds 7 mean bytes/connection 3.44968 fetches/sec, 24.1478 bytes/sec msecs/connect: 0.113483 mean, 0.948 max, 0.024 min msecs/first-response: 20156.5 mean, 32294.2 max, 9607.34 min HTTP response codes: code 200 -- 207
tornado.gen是一個生成器(關於生成器參見“python生成器,函式,陣列” ),
yield關鍵字的作用是返回控制,非同步任務執行完畢後,程式在yield的地方恢復。
可以看到使用生成器,非同步後業務處理不是在回撥函式中完成的,看起來像同步處理一樣,程式碼邏輯更清晰。
使用生成器和回撥函式非同步請求是一樣的。
6.非同步請求的適用場景
請求處理邏輯複雜耗時,或長時間請求資料庫的時候,非同步請求可以大幅提升併發請求效率。
同時綜合考慮快取,業務邏輯放在客戶端等手段,來緩解伺服器壓力。