tornado 原始碼閱讀-初步認識

發表於2016-07-03

序言

ioloop

原始碼分析

1.回撥 callbacks

他是ioloop回撥的基礎部分,通過IOLoop.instance().add_callback()新增到self._callbacks
他們將在每一次loop中被執行.

主要用途是將邏輯分塊,在適合時機將包裝好的callback新增到self._callbacks讓其執行.

例如ioloop中的add_future

future物件得到result的時候會呼叫future.add_done_callback新增的callback,再將其轉至ioloop執行

2.定時器 due_timeouts

這是定時器,在指定的事件執行callback.
跟1中的callback類似,通過IOLoop.instance().add_callback

在每一次迴圈,會計算timeouts回撥列表裡的事件,執行已到期的callback.
當然不是無節操的迴圈.

因為poll操作會阻塞到有io操作發生,所以只要計算最近的timeout,
然後用這個時間作為self._impl.poll(poll_timeout)poll_timeout ,
就可以達到按時執行了

但是,假設poll_timeout的時間很大時,self._impl.poll一直在堵塞中(沒有io事件,但在處理某一個io事件),
那新增剛才1中的callback不是要等很久才會被執行嗎? 答案當然是不會.
ioloop中有個waker物件,他是由兩個fd組成,一個讀一個寫.
ioloop在初始化的時候把waker繫結到epoll裡了,add_callback時會觸發waker的讀寫.
這樣ioloop就會在poll中被喚醒了,接著就可以及時處理timeout callback

用這樣的方式也可以自己封裝一個小的定時器功能玩玩

3.io事件的event loop

處理epoll事件的功能
通過IOLoop.instance().add_handler(fd, handler, events)繫結fd event的處理事件
httpserver.listen的程式碼內,
netutil.py中的netutil.pyadd_accept_handler繫結accept handler處理客戶端接入的邏輯

如法炮製,其他的io事件也這樣繫結,業務邏輯的分塊交由ioloopcallbackfuture處理

關於epoll的用法的內容.詳情見我第一篇文章吧,哈哈

總結

ioloop由callback(業務分塊), timeout callback(定時任務) io event(io傳輸和解析) 三塊組成,互相配合完成非同步的功能,構建gen,httpclient,iostream等功能

串聯大致的流程是,tornado 繫結io event,處理io傳輸解析,傳輸完成後(結合Future)回撥(callback)業務處理的邏輯和一些固定操作 . 定時器則是較為獨立的模組

Futrue

個人認為Futuretornado僅此ioloop重要的模組,他貫穿全文,所有非同步操作都有他的身影
顧名思義,他主要是關注日後要做的事,類似jqueryDeferred

一般的用法是通過ioloopadd_future定義futuredone callback,
futureset_result的時候,futuredone callback就會被呼叫.
從而完成Future的功能.

具體可以參考gen.coroutine的實現,本文後面也會講到

他的組成不復雜,只有幾個重要的方法
最重要的是 add_done_callback , set_result

tornadoFutureioloop,yield實現了gen.coroutine

1. add_done_callback

ioloopcallback類似 , 儲存事件完成後的callbackself._callbacks

2.set_result

設定事件的結果,並執行之前儲存好的callback

為了驗證之前所說的,上一段測試程式碼

執行結果:

tornado 原始碼閱讀-初步認識

gen.coroutine

接著繼續延伸,看看coroutine的實現
gen.coroutine實現的功能其實是將原來的callback的寫法,用yield的寫法代替. 即以yield為分界,將程式碼分成兩部分.
如:

執行結果:

tornado 原始碼閱讀-初步認識

原始碼分析

接下來分析下coroutine的實現

如原始碼所示,func執行的結果是GeneratorType ,yielded = next(result),
執行至原函式的yield位置,返回的是原函式func內部 yield 右邊返回的物件(必須是FutureFuturelist)給yielded.
經過Runner(result, future, yielded) 對yielded進行處理.
在此就 貼出Runner的程式碼了.
Runner初始化過程,呼叫handle_yield, 檢視yielded是否已done了,否則add_future執行Runnerrun方法,
run方法中如果yielded物件已完成,用對它的gen呼叫send,傳送完成的結果.
所以yielded在什麼地方被set_result非常重要,
當被set_result的時候,才會send結果給原func,完成整個非同步操作

詳情可以檢視tornado 中重要的物件 iostream,原始碼中iostream的 _handle_connect,如此設定了連線的result.

最後貼上一個簡單的測試程式碼,演示coroutine,future的用法

執行結果:

tornado 原始碼閱讀-初步認識

為什麼程式碼中個yield都起作用了? 因為Runner.run裡,最後繼續用handle_yield處理了send後返回的yielded物件,意思是func裡可以有n幹個yield操作

總結

至此,已完成tornado中重要的幾個模組的流程,其他模組也是由此而來.寫了這麼多,越寫越卡,就到此為止先吧,

最後的最後的最後

啊~~~~~~好想有份工作女朋友啊~~~~~

相關文章