1.爲何要使用異步web服務
使用異步非阻塞請求,併發處理更高效。html
2.同步與異步請求比較
同步請求時,web服務器進程是阻塞的,也就是說當一個請求被處理時,服務器進程會被掛起直至請求完成。python
異步請求時,web服務器進程在等待請求處理過程當中,讓I/O循環打開,以便服務於其餘請求,請求處理完成後繼續執行回調函數或生成器,而再也不是等待請求過程當中掛起進程。整個過程是異步的。web
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方法來主動關閉鏈接。async
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.異步請求的適用場景 請求處理邏輯複雜耗時,或長時間請求數據庫的時候,異步請求能夠大幅提高併發請求效率。 同時綜合考慮緩存,業務邏輯放在客戶端等手段,來緩解服務器壓力。