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)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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