這幾天看了Tornado的源碼,寫這篇文章以做總結(jié)。本文采用Tornado v1.2版本的源碼,討論Tornado運(yùn)行過(guò)程而不沉浸入代碼實(shí)現(xiàn)。
主要模塊分析
|---web.py (應(yīng)用框架層)
|---httpserver.py ( HTTP TCP層 )
|---ioloop.py (數(shù)據(jù)處理層)
|---iostream.py
web.py 實(shí)現(xiàn)了tornado的web框架,定義了Application, RequestHandler兩個(gè)類。Application是一個(gè)單例,注冊(cè)了全局路由,服務(wù)器轉(zhuǎn)發(fā)過(guò)來(lái)的請(qǐng)求調(diào)用該類的__call__()。RequestHandler主要功能主要是將handler和url進(jìn)行映射。
httpserver.py 建立http服務(wù)器,并解析http請(qǐng)求。
ioloop.py 主要是將底層的epoll或者說(shuō)是其他的IO多路復(fù)用封裝作異步事件來(lái)處理。
iostream.py 對(duì)sock進(jìn)行封裝。
Tornado運(yùn)行流程:從請(qǐng)求到響應(yīng)
從main函數(shù)出發(fā):
application = web.Application([
(r"/", MainPageHandler),
])
http_server = httpserver.HTTPServer(application)
http_server.listen(8080)
ioloop.IOLoop.instance().start()
首先實(shí)例化Application(),并設(shè)置了路由表。
將application實(shí)例傳入HTTPServer
# httpserver.HTTPServer初始化
def __init__(self, request_callback, no_keep_alive=False...):
# request_callback 便是傳入的application
self.request_callback = request_callback
...
接著http_server監(jiān)聽8080端口,listen()先綁定了端口和地址(self.bind),接著是一系列的判斷之后調(diào)用self.start()
# start()函數(shù)最重要的一件事就是將self._sockets加入ioloop
def start(self, num_processes=1):
if ...
...
self.io_loop = ioloop.IOLoop.instance()
# 當(dāng)有對(duì)應(yīng)請(qǐng)求來(lái)時(shí),ioloop將當(dāng)前線程拉起,
# 執(zhí)行回調(diào)函數(shù) self._handle_events()
self.io_loop.add_handler(
self._socket.fileno(), self._handle_events,
ioloop.IOLoop.READ)
return
os.waitpid(-1, 0)
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)
接下來(lái)看self._handle_events()
def _handle_events(self, fd, events):
while True:
try:
connection, address = self._socket.accept()
...
try:
if ...
# 將sock封裝
stream = iostream.IOStream(connection, io_loop=self.io_loop)
# 實(shí)例化HTTPConnection,回調(diào)函數(shù)self.request_callback
# 便是application,HTTPConnection實(shí)現(xiàn)對(duì)請(qǐng)求的解析
HTTPConnection(stream, address, self.request_callback,
self.no_keep_alive, self.xheaders)
...
這里需要注意一下HTTPConnection初始化過(guò)程
def __init__(self, stream, address, request_callback, no_keep_alive=False,
xheaders=False):
self.stream = stream
self.address = address
self.request_callback = request_callback
self.no_keep_alive = no_keep_alive
self.xheaders = xheaders
self._request = None
self._request_finished = False
# Save stack context here, outside of any request. This keeps
# contexts from one request from leaking into the next.
self._header_callback = stack_context.wrap(self._on_headers)
self.stream.read_until("\r\n\r\n", self._header_callback)
self._on_headers()實(shí)現(xiàn)對(duì)請(qǐng)求解析出請(qǐng)求頭然后組裝成HTTPRequest,最后將組裝后的請(qǐng)求使用request_callback回調(diào)
def _on_headers(self, data):
try:
...
# 解析出headers
headers = httputil.HTTPHeaders.parse(data[eol:])
self._request = HTTPRequest(
connection=self, method=method, uri=uri, version=version,
headers=headers, remote_ip=self.address[0])
...
# 在這里調(diào)用了application
self.request_callback(self._request)
...
所以我們便回到了__call__(),看看__call__()干了什么
def __call__(self, request):
...
# 獲取handlers,根據(jù)官方文檔,如果有多個(gè),取第一個(gè)
handlers = self._get_host_handlers(request)
...
handler = spec.handler_class(self, request, **spec.kwargs) # 找到handler
handler._execute(transforms, *args, **kwargs) # 執(zhí)行
return handler
到了這里,便是用_execute執(zhí)行handler的業(yè)務(wù)邏輯,那么便到此結(jié)束了嗎?答案是否定的,我們還忘了一個(gè)最重要的ioloop,其實(shí)我們?cè)趆ttp_server.start()便使用到ioloop了,下面看看ioloop是怎么輪詢的
while True:
...
try:
# 這里開始循環(huán)輪詢,具體干了什么我也不知道
event_pairs = self._impl.poll(poll_timeout)
...
self._events.update(event_pairs)
while self._events:
fd, events = self._events.popitem()
try:
# 這里才是重點(diǎn),經(jīng)過(guò)前面的鋪墊我們有了一個(gè)events,接著便是調(diào)用對(duì)應(yīng)
# 的handlers,當(dāng)然我們的這個(gè)handlers不是平白無(wú)故跳出來(lái)的,還記得
# start()最重要的功能是什么嗎?
self._handlers[fd](fd, events)
到了這里,對(duì)于Tornado從一個(gè)請(qǐng)求到一個(gè)響應(yīng)的整個(gè)過(guò)程涉及到的主內(nèi)容基本過(guò)了一遍。在程序初始化階段,application完成路由表的注冊(cè),httpserver建立、綁定端口并開始監(jiān)聽,同時(shí)將sockets注冊(cè)到ioloop中,程序開始后,ioloop不斷輪詢,當(dāng)檢測(cè)到有請(qǐng)求后,通過(guò)其文件描述符(sock)找到對(duì)應(yīng)的handlers,此時(shí)將執(zhí)行注冊(cè)時(shí)的回調(diào)函數(shù)_handle_events(),解析headers,重新封裝httprequire后傳給application,application根據(jù)request找到對(duì)應(yīng)的handler,并執(zhí)行響應(yīng)的業(yè)務(wù)邏輯,之后便是生成相應(yīng)的response。放圖一張(源自:)
總結(jié)
從一開始的同步、異步、阻塞、非阻塞、多路復(fù)用等基本概念出發(fā),參考上一篇文章,了解了傳統(tǒng)網(wǎng)絡(luò)模型中的進(jìn)程模型、線程模型,當(dāng)訪問(wèn)人數(shù)越來(lái)越多時(shí),傳統(tǒng)的網(wǎng)絡(luò)模型,一個(gè)進(jìn)程或一個(gè)線程只能處理一個(gè)鏈接便不再適用。于是提出了多路復(fù)用的概念,tornado主要討論了epoll的使用,同時(shí),tornado適用iostream實(shí)現(xiàn)異步讀寫,以達(dá)到高并發(fā)、高性能的目的。
相信隨著學(xué)習(xí)的深入,對(duì)這篇文章的理解以及修改將一直進(jìn)行下去。