源代碼 Tornado 1-2-1
IOLoop類是Tornado的邊緣觸發事件驅動模型,在Linux平臺下面封裝的是epoll模型,這個類的代碼也很簡單,比Nginx好看多了。
先看屬性:
_handlers保存(fd,handler)的映射關係,_events保存就緒的fd以及對應的events事件(讀/寫/錯誤),_blocking_signal_threshold表示超時閾值,如果不爲None,當select.poll()返回後,調用fd對應的handler工作時間超過這個值時,就會觸發SIGALRM信號,set_blocking_signal_threshold可以設置這個閾值,並安裝SIGALRM信號處理函數。_callbacks 存儲的是一些函數對象(回調函數),具體作用暫且不懂。
方法分析:
1、初始化:注意管道的作用,和Nginx的進程模型類似,管道也是用於其他進程向ioloop.start()進程發送命令。Tornado中不解析命令,只是簡單的退出監聽循環。
def __init__(self, impl=None):
.....
# hp: 管道,用來發送命令給epoll_wait進程. 在這裏只是爲了喚醒epoll.poll()/epoll_wait,接着退出ioloop循環,停止監聽事件
if os.name != 'nt':
r, w = os.pipe()
self._set_nonblocking(r)
self._set_nonblocking(w)
self._set_close_exec(r)
self._set_close_exec(w)
self._waker_reader = os.fdopen(r, "rb", 0) # hp: fdopen convert fileno to FILE*, bufsize=0
self._waker_writer = os.fdopen(w, "wb", 0)
else:
.....
# hp: 只監聽管道讀事件,讀取其他進程發送給ioloop進程的命令,使得ioloop退出監聽循環
self.add_handler(r, self._read_waker, self.READ)
2、add_handler, update_handler、remove_handler就是簡單的對epoll_ctl的封裝,沒有什麼特殊的地方。
3、start函數,進程的監聽循環,循環監聽所有的fd,並調用fd對應的handler,和Nginx類似。
def start(self):
if self._stopped:
self._stopped = False
return
self._running = True
while True:
poll_timeout = 0.2
callbacks = self._callbacks
self._callbacks = []
for callback in callbacks:
self._run_callback(callback)
if self._callbacks: # hp: 現在還沒有看
poll_timeout = 0.0
# hp: 定時機制和Nginx一模一樣
if self._timeouts: # hp: 定時事件列表
now = time.time()
# hp: 處理超時的事件
while self._timeouts and self._timeouts[0].deadline <= now:
timeout = self._timeouts.pop(0)
self._run_callback(timeout.callback)
if self._timeouts:
milliseconds = self._timeouts[0].deadline - now
# hp: 時間設置和Nginx一樣,取下一個定時事件的時間
poll_timeout = min(milliseconds, poll_timeout)
if not self._running: # hp: 調用stop()設置爲false,通過管道給epoll發送消息,epoll醒來便退出循環
break
if self._blocking_signal_threshold is not None:
# clear alarm so it doesn't fire while poll is waiting for
# events.
# hp: 清空之前的定時器
signal.setitimer(signal.ITIMER_REAL, 0, 0)
try:
event_pairs = self._impl.poll(poll_timeout)
except Exception, e:
...
# hp: 安裝定時器
if self._blocking_signal_threshold is not None:
signal.setitimer(signal.ITIMER_REAL,
self._blocking_signal_threshold, 0)
self._events.update(event_pairs)
while self._events:
fd, events = self._events.popitem()
try:
# hp: 處理激活的fd
self._handlers[fd](fd, events)
except
# 退出監聽循環
# reset the stopped flag so another start/stop pair can be issued
self._stopped = False
if self._blocking_signal_threshold is not None:
# hp: 清除定時器
signal.setitimer(signal.ITIMER_REAL, 0, 0)
4、stop函數:自己還不太習慣在面向對象編程中操作進程(習慣用C語言),比如:如何調用stop()函數,當前進程已經在start()中的epoll.poll中阻塞了,當前進程已經不能調用stop(),其他子進程中的ioloop對象處於不同的進程空間,只用把IOLoop的實例放入共享內存中,其他進程才能調用stop()從而終止當前進程的epoll循環。
def stop(self):
self._running = False
self._stopped = True
self._wake()
def _wake(self):
'''hp: 退出當前的epoll_wait調用'''
try:
self._waker_writer.write("x")
except IOError:
pass
參考