1. 程式人生 > >tornado非同步web請求

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.非同步請求的適用場景
請求處理邏輯複雜耗時,或長時間請求資料庫的時候,非同步請求可以大幅提升併發請求效率。
同時綜合考慮快取,業務邏輯放在客戶端等手段,來緩解伺服器壓力。