引言:上一章講了關於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...
