Tornado 之 IOLoop類分析

    源代碼 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

參考


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章