以前在公司的一個模塊,須要從另外一處url取得數據,我使用了Python的一個很著名的lib,叫作requests。可是這樣作極大的下降了程序的性能,由於tornado是單線程的,它使用了所謂的reactor模式,底層使用epoll監聽每一個tcp鏈接,上層再通過封裝,接受HTTP請求。因此,tornad其實是單線程的。html
在實際的場景中,常常採用nginx反向代理的模式,而後服務器開啓多個tornado進程,接受nginx發送過來的請求。react
剛纔的問題主要是,由於requests是阻塞的,因此當我發出一個post請求,整個tornado進程就阻塞了,此時該進程不能接受任何的其餘請求。nginx
想一想咱們的服務器總共才十幾個tornado進程,可能要應對上千的併發量,因此阻塞一個進程對咱們是巨大的損失。web
tornado內置了異步的模塊,例如AsyncHttpClient,它的使用以下:數據庫
class MainHandler(tornado.web.RequestHandler): @tornado.web.asynchronous def get(self): http = tornado.httpclient.AsyncHTTPClient() http.fetch("http://friendfeed-api.com/v2/feed/bret", callback=self.on_response) def on_response(self, response): if response.error: raise tornado.web.HTTPError(500) json = tornado.escape.json_decode(response.body) self.write("Fetched " + str(len(json["entries"])) + " entries " "from the FriendFeed API") self.finish()
那麼,tornado的gen是怎麼回事? 能夠看到,上面的代碼中使用了回調函數,可是回調函數有一個致命的問題,若是邏輯很是複雜,那麼咱們的程序可能嵌套多層回調,形成所謂的「回調地獄」。json
事實上,tornado的gen模塊,就是爲了改善這一問題。例如:api
class GenAsyncHandler(RequestHandler): @gen.coroutine def get(self): http_client = AsyncHTTPClient() response = yield http_client.fetch("http://example.com") do_something_with_response(response) self.render("template.html")
從上面能夠看出,gen模塊的最大做用,就是將異步代碼的編寫進行改進,使其看起來像同步。服務器
上面的代碼執行時,遇到yield後面的阻塞調用則暫停,而後去執行其餘請求,等該數據返回時,再繼續處理這裏。併發
這樣就防止了一個IO操做阻塞整個進程。異步
在實際應用中,對於可能阻塞的操做(例如查詢量較大的數據庫查詢),最好使用異步。