引言:上一章講了關於HTTPServer的原理,這一次通過分析源碼來了解更多的細節
看看HTTPServer類的組織結構:
HTTPServer的主要工作
一.提供了一些基礎的比如說listen,bind此類共有操作
二.完成了一個 _handle_events()的公有回調函數,此函數是 IOLoop的基礎,此函數為每一個連接創建一個單獨的 IOStream 對象
三.start函數,啟動HTTPServer,並設置相應的參數(如根據CPU個數來設置進程數等)
從HTTPServer類的構造函數可以看出,最重要的參數是設置回調函數,此回調函數用於處理request對象
每次有HTTP的請求,都會通過HTTPConnection 封裝一個HTTPRequest對象,這個對象包含了HTTP請求的所有信息
所以在HTTPServer層,需要對這個對象進行一番處理后調用 request.write將結果返回給客戶端
此回調函數會先注冊到HTTPServer,然后注冊到HTTPConnection 里面,因為request這個對象是由HTTPConnection對象產生
def _handle_events(self, fd, events): while True: try: connection, address = self._socket.accept() except socket.error, e: if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN): return raise #SSL 選項 if self.ssl_options is not None: assert ssl, "Python 2.6+ and OpenSSL required for SSL" try: connection = ssl.wrap_socket(connection, server_side=True, do_handshake_on_connect=False, **self.ssl_options) except ssl.SSLError, err: if err.args[0] == ssl.SSL_ERROR_EOF: return connection.close() else: raise except socket.error, err: if err.args[0] == errno.ECONNABORTED: return connection.close() else: raise try: if self.ssl_options is not None: stream = iostream.SSLIOStream(connection, io_loop=self.io_loop) else: #為每一個 connection 創建一個 iostream 實例,以后的IO操作由此實例負責 #IOLoop只負責 accept這個連接 stream = iostream.IOStream(connection, io_loop=self.io_loop) #將 stream對象和對應的 address , callback加入到HTTPConnection 中 #HTTPConnection稍后會有介紹 #這里的 request_callback 是由Demo里 httpserver.HTTPServer(handle_request) 傳遞進來 #現代的 HTTP 框架都采用這種模式 #創建一個 handle_request 這個 回調函數嵌套的注冊到下層,直到真正處理request #一般情況是回調繼續傳遞下去直到遇到一個類方法能夠傳遞 request 對象給這個函數 HTTPConnection(stream, address, self.request_callback, self.no_keep_alive, self.xheaders) except: logging.error("Error in connection callback", exc_info=True)
通過調用HTTPConnection,然后傳遞stream,address和request_callback到HTTPConnection可以看到,處理request的回調函數注冊到了HTTPConnection.
還需要注意的地方就是,每一次有一個連接的到來,IOLoop都只負責處理accept此連接,然后后面的IO操作就交給IOStream來處理
在start()函數中,會為每個進程創建一個單獨的IOLoop,然后此IOLoop的回調函數統一采用_handle_events()
_handle_events()函數的處理流程總體來說是這樣:
1.注冊到本進程的IOLoop中
2.當有事件發生,只注冊了READ事件,也就是只接受新連接,每次有連接到來,都回調_handle_events()
3.accept此新連接,然后為此新連接創建一個IOStream對象,以后此IOStream負責本連接的所有IO操作,這里是一層抽象,實際
在IOStream的讀寫事件也是注冊到了本進程的IOLoop中,只不過回調函數不一樣,因為注冊時候的描述符不同。
調用方式是通過handle[fd]()這種方式調用,所以對於監聽套接口每次都只會調用_handle_events()而對於其他的IOStream的連接
fd會調用在read_bytes(),read_utils()中注冊的回調函數
再看看在HTTPServer中的 start()函數
def start(self, num_processes=1): assert not self._started self._started = True if num_processes is None or num_processes <= 0: num_processes = _cpu_count() if num_processes > 1 and ioloop.IOLoop.initialized(): logging.error("Cannot run in multiple processes: IOLoop instance " "has already been initialized. You cannot call " "IOLoop.instance() before calling start()") num_processes = 1 if num_processes > 1: logging.info("Pre-forking %d server processes", num_processes) #根據 處理器個數來決定 fork多少個進程 for i in range(num_processes): if os.fork() == 0:# fork() == 0 表示子進程 import random from binascii import hexlify try: # If available, use the same method as # random.py seed = long(hexlify(os.urandom(16)), 16) except NotImplementedError: # Include the pid to avoid initializing two # processes to the same value seed(int(time.time() * 1000) ^ os.getpid()) random.seed(seed) #為每個進程創建一個IOLoop實例 self.io_loop = ioloop.IOLoop.instance() #為每個IOLoop 添加回調函數,這里采用統一回調方式,和IOStream 一樣 self.io_loop.add_handler( self._socket.fileno(), self._handle_events, ioloop.IOLoop.READ) return os.waitpid(-1, 0)#預防僵屍進程,Unix 環境編程介紹很多 else: if not self.io_loop: self.io_loop = ioloop.IOLoop.instance() self.io_loop.add_handler(self._socket.fileno(), self._handle_events, ioloop.IOLoop.READ)
可以在代碼注釋中看到會根據每一個CPU一個IOLoop實例的方式處理,至於中間的產生隨機數是為什么,如果有人知道請告知我
在start()函數的最后可以看到add_handle將監聽套接口和_handle_events()函數注冊到了IOLoop中,這就是上面所講的HTTPServer處理連接的過程
總結:總結不想寫了,土逼Continue...