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使用詳解”)進行壓力測試,結果如下:
同步壓力測試:
[root@51dev 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.異步請求的適用場景
請求處理邏輯復雜耗時,或長時間請求數據庫的時候,異步請求可以大幅提升並發請求效率。
同時綜合考慮緩存,業務邏輯放在客戶端等手段,來緩解服務器壓力。
參考資料:http://docs.pythontab.com/tornado/introduction-to-tornado/ch5.html