這幾天看了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)行下去币励。