why asynchronous
tornado是乙個非同步web framework,說是非同步,是因為tornado server與client的網路互動是非同步的,底層基於io event loop。但是如果client請求server處理的handler裡面有乙個阻塞的耗時操作,那麼整體的server效能就會下降。
在上面的例子中,tornado server的整體效能依賴於訪問google的時間,如果訪問google的時間比較長,就會導致整體server的阻塞。所以,為了提公升整體的server效能,我們需要一套機制,使得handler處理都能夠通過非同步的方式實現。
幸運的是,tornado提供了一套非同步機制,方便我們實現自己的非同步操作。當handler處理需要進行其餘的網路操作的時候,tornado提供了乙個async http client用來支援非同步。
上面的例子,主要有幾個變化:
asynchronous flaw
非同步操作是乙個很強大的操作,但是它也有一些缺陷。最主要的問題就是在於callback導致了**邏輯的拆分。對於程式設計師來說,同步順序的想法是乙個很自然的習慣,但是非同步打破了這種順序性,導致**編寫的困難。這點,對於寫nodejs的童鞋來說,可能深有體會,如果所有的操作都是非同步,那麼最終我們的**可能寫成這樣:
也就是說,我們可能會寫出callback巢狀callback的情況,這個極大的會影響**的閱讀與流程的實現。
synchronous
我個人認為,非同步拆散了**流程這個問題不大,畢竟如果乙個邏輯需要過多的巢狀callback來實現的話,那麼我們就需要考慮這個邏輯是否合理了,所以非同步一般也不會有過多的巢狀層次。
雖然我認為非同步的callback問題不大,但是如果仍然能夠有一套機制,使得非同步能夠順序化,那麼對於**邏輯的編寫來說,會方便很多。tornado有一些機制來實現。
yield
在python裡面如果乙個函式內部實現了yield,那麼這個函式就不是函式了,而是乙個生成器,它的整個執行機制也跟普通函式不一樣,舉乙個例子:
deftest_yield():print'yield 1'a=yield'yielded'print'over',at=test_yield()print'main',type(t)ret=t.send(none)printrettry:t.send('hello yield')exceptstopiteration:print'yield over'
輸出結果如下:
main yield 1yieldedover hello yieldyield over
從上面可以看到,test_yield是乙個生成器,當它第一次呼叫的時候,只是生成了乙個generator,不會執行。當第一次呼叫send的時候,生成器被resume,開始執行,然後碰到yield,就掛起,等待下一次被send喚醒。當生成器執行完畢,會丟擲stopiteration異常,供外部send的地方知曉。
因為yield很方便的提供了一套函式掛起,執行的機制,所以我們能夠通過yield來將原本是非同步的流程變成同步的。
gen
tornado有乙個gen模組,提供了task和callback/wait機制用來支援同步模型,以task為例:
可以看到,tornado的gen模組就是通過yield來進行同步化的。主要有如下需要注意的地方:
可以看到,使用gen和yield之後,原先的非同步邏輯變成了同步流程,在**的閱讀性上面就有不錯的提公升,不過對於不熟悉yield的童鞋來說,開始反而會很迷惑,不過只要理解了yield,那就很容易了。
greenlet
雖然yield很強大,但是它只能掛起當前函式,而無法掛起整個堆疊,這個怎麼說呢,譬如我想實現下面的功能:
defa():yield1defb():a()t=b()t.send(none)
這個通過yield是無法實現的,也就是說,a裡面使用yield,它是乙個生成器,但是a的掛起無法將b也同時掛起。也就是說,我們需要一套機制,使得堆疊在任何地方都能夠被掛起和恢復,能方便的進行棧切換,而這套機制就是coroutine。
最開始使用coroutine是在lua裡面,它原生提供了coroutine的支援。然後在使用luajit的時候,發現內部是基於fiber(win)和context(unix),也就是說,不光lua,其實c/c++我們也能實現coroutine。現在研究了go,也是內建coroutine,並且這裡極力推薦一篇slide
。python沒有原生提供coroutine,不知道以後會不會有。但有乙個greenlet,能幫我們實現coroutine機制。而且還有人專門寫好了tornado與greenlet結合的模組,叫做greenlet_tornado
,使用也很簡單
classmainhandler(tornado.web.requesthandler):@greenlet_asynchronousdefget(self):response=greenlet_fetch('')self.write("hello world")self.finish()
可以看到,使用greenlet,能更方便的實現**邏輯,這點比使用gen更方便,因為這些連寫**的童鞋都不用去糾結yield問題了。
總結
這裡只是簡單的介紹了tornado的一些非同步處理流程,以及將非同步同步化的一些方法。另外,這裡舉得例子都是網路http請求方面的,但是server處理請求的時候,可能還需要進行資料庫,本地檔案的操作,而這些也是同步阻塞耗時操作,同樣可以通過非同步來解決的,這裡就不詳細說明了。
Tornado非同步模式
先介紹下背景 由於工作需要,前段時間又寫了一段爬蟲去獲取和更新一些資料。之前爬蟲主要用scrapy框架批量爬取一些頁面資料,或者用gevent呼叫目標站點的介面。偶然看到了tornado,聽說這個框架很強大,所以打算這次爬蟲用tornado試試。不足之處,歡迎指正。總的來說,tornado是pyth...
Tornado 非同步socketTCP通訊
tornado 有 tcpclient 和 tcpserver 兩個類,可用於實現 tcp 的客戶端和服務端。事實上,這兩個類都是對iostream的簡單包裝。iostream 是 client 與 server 之間的 tcp 通道。被動等待建立 iostream 的一方是 server,主動找對...
tornado中非同步request使用
使用httprequest太無腦了,太莽了,當希望使用非同步request時,首先引入asynchttprequest from tornado.httpclient import asynchttpclient將介面中的方法新增上tornado的協程符 coroutine根據request的方式 ...