Tornado源碼閱讀

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市珊拼,隨后出現(xiàn)的幾起案子食呻,更是在濱河造成了極大的恐慌,老刑警劉巖澎现,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仅胞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡昔头,警方通過(guò)查閱死者的電腦和手機(jī)饼问,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)揭斧,“玉大人莱革,你說(shuō)我怎么就攤上這事《锟” “怎么了盅视?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)旦万。 經(jīng)常有香客問(wèn)我闹击,道長(zhǎng),這世上最難降的妖魔是什么成艘? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任赏半,我火速辦了婚禮,結(jié)果婚禮上淆两,老公的妹妹穿的比我還像新娘断箫。我一直安慰自己,他們只是感情好秋冰,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布仲义。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪埃撵。 梳的紋絲不亂的頭發(fā)上赵颅,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天,我揣著相機(jī)與錄音暂刘,去河邊找鬼饺谬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛鸳惯,可吹牛的內(nèi)容都是我干的商蕴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼芝发,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了苛谷?” 一聲冷哼從身側(cè)響起辅鲸,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎腹殿,沒(méi)想到半個(gè)月后独悴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锣尉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年刻炒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片自沧。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坟奥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拇厢,到底是詐尸還是另有隱情爱谁,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布孝偎,位于F島的核電站访敌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏衣盾。R本人自食惡果不足惜寺旺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望势决。 院中可真熱鬧阻塑,春花似錦、人聲如沸徽龟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至传透,卻和暖如春耘沼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背朱盐。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工群嗤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兵琳。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓狂秘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親躯肌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子者春,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容