成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

tornado源碼解析之IOLoop

Lsnsh / 739人閱讀

摘要:最大的特點(diǎn)就是其支持異步,所以它有著優(yōu)異的性能。的代碼結(jié)構(gòu)可以在其官網(wǎng)了解,本文著重分析的實(shí)現(xiàn)。事件驅(qū)動(dòng)模型的大致思路的方法用于啟動(dòng)事件循環(huán)。行文比較草率,如有錯(cuò)誤和不足之處,敬請(qǐng)指正。

0. 簡(jiǎn)介

tornado是一個(gè)用Python語(yǔ)言寫(xiě)成的Web服務(wù)器兼Web應(yīng)用框架,由FriendFeed公司在自己的網(wǎng)站FriendFeed中使用,被Facebook收購(gòu)以后框架以開(kāi)源軟件形式開(kāi)放給大眾。

tornado最大的特點(diǎn)就是其支持異步IO,所以它有著優(yōu)異的性能。下表是和一些其他Web框架與服務(wù)器的對(duì)比:(處理器為 AMD Opteron, 主頻2.4GHz, 4核) (來(lái)源wikipedia)

服務(wù) 部署 請(qǐng)求/每秒
Tornado nginx, 4進(jìn)程 8213
Tornado 1個(gè)單線程進(jìn)程 3353
Django Apache/mod_wsgi 2223
web.py Apache/mod_wsgi 2066
CherryPy 獨(dú)立 785

先來(lái)看看hello world的例子。^_^

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options

define("port", default=8888, help="run on the given port", type=int)


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")


def main():
    tornado.options.parse_command_line()
    application = tornado.web.Application([
        (r"/", MainHandler),
    ])
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.current().start()


if __name__ == "__main__":
    main()

運(yùn)行:

$ python3 helloworld.py

我們就得到一個(gè)web server監(jiān)聽(tīng)在8888端口。用curl命令get一下,就返回了"Hello, world"。

tornado的代碼結(jié)構(gòu)可以在其官網(wǎng)了解,本文著重分析IOLoop的實(shí)現(xiàn)。

1. IOLoop 1.1 http交互的大致過(guò)程

介紹IOLoop之前我們先看看http server和http client交互的一個(gè)大致過(guò)程。

server端監(jiān)聽(tīng)在某個(gè)端口,client端發(fā)送請(qǐng)求過(guò)來(lái),server處理后返回,然后繼續(xù)等待下一個(gè)請(qǐng)求,周而復(fù)始。如果用socket那一坨來(lái)描述的話就是:

1. server.py
================================================================
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(address)
s.listen(backlog)
While True:
    connection = s.accept()
    do_something()
    connection.send()
    connection.close()
    
2. client.py
=================================================================
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect()
s.send()
s.recv()
s.close()
1.2 聊聊阻塞與非阻塞

所謂阻塞,就是進(jìn)程正在等待某些資源(如IO),而處于等待運(yùn)行的狀態(tài)(不占用CPU資源)。比如connect(("google.com", 80))返回之前進(jìn)程都是阻塞的,在它下面的語(yǔ)句得不到執(zhí)行,除非connect返回。

很顯然阻塞式的IO模型有個(gè)缺點(diǎn)就是并發(fā)量不大,試想如果server進(jìn)程在do_something()處阻塞,而這時(shí)另外有個(gè)客戶端試圖連進(jìn)來(lái),則可能得不到響應(yīng)。

提高并發(fā)量有幾種實(shí)現(xiàn)方式:多線程(一個(gè)連接fork一個(gè)線程去處理);多進(jìn)程(一個(gè)連接fork一個(gè)子進(jìn)程去處理)(apache);事件驅(qū)動(dòng)(nginx, epoll)等。tornado就是基于epoll(Linux)事件驅(qū)動(dòng)模型實(shí)現(xiàn)的。

當(dāng)然它們有各自的優(yōu)缺點(diǎn),此文不詳述,有興趣的讀者可以自行g(shù)oogle之。^_^

關(guān)于IO模型,epoll, 同步,異步,阻塞,非阻塞的概念,可以參考這兩篇文章:
https://segmentfault.com/a/11...

http://blog.csdn.net/historya...

1.3 IOLoop實(shí)現(xiàn) 1.3.1 IOLoop配置

前文說(shuō)到tornado是基于epoll事件驅(qū)動(dòng)模型,也不完全正確,tornado實(shí)際上是根據(jù)平臺(tái)選擇底層驅(qū)動(dòng)。請(qǐng)看IOLoop類的configurable_default方法:

    @classmethod
    def configurable_default(cls):
        if hasattr(select, "epoll"):
            from tornado.platform.epoll import EPollIOLoop
            return EPollIOLoop
        if hasattr(select, "kqueue"):
            # Python 2.6+ on BSD or Mac
            from tornado.platform.kqueue import KQueueIOLoop
            return KQueueIOLoop
        from tornado.platform.select import SelectIOLoop
        return SelectIOLoop

這里的IOLoop實(shí)際上是個(gè)通用接口,根據(jù)不同平臺(tái)選擇:linux->epoll,BSD->kqueue,如果epoll和kqueue都不支持則選擇select(性能要差些)。

class IOLoop(Configurable):IOLoop繼承了Configurable類,Configurable類的__new__方法調(diào)用了configured_class方法:

    def __new__(cls, *args, **kwargs):
        base = cls.configurable_base()
        init_kwargs = {}
        if cls is base:
            impl = cls.configured_class()
            if base.__impl_kwargs:
                init_kwargs.update(base.__impl_kwargs)
        else:
            impl = cls
        init_kwargs.update(kwargs)
        instance = super(Configurable, cls).__new__(impl)
        # initialize vs __init__ chosen for compatibility with AsyncHTTPClient
        # singleton magic.  If we get rid of that we can switch to __init__
        # here too.
        instance.initialize(*args, **init_kwargs)
        return instance

configured_class方法又調(diào)用了configurable_default方法:

    @classmethod
    def configured_class(cls):
        # type: () -> type
        """Returns the currently configured class."""
        base = cls.configurable_base()
        if cls.__impl_class is None:
            base.__impl_class = cls.configurable_default()
        return base.__impl_class

所以當(dāng)初始化一個(gè)IOLoop實(shí)例的時(shí)候就給IOLoop做了配置,根據(jù)不同平臺(tái)選擇合適的驅(qū)動(dòng)。

1.3.2 IOLoop實(shí)例化

下面我們來(lái)看IOLoop的實(shí)例化函數(shù):

    # Global lock for creating global IOLoop instance
    _instance_lock = threading.Lock()
    @staticmethod
    def instance():
        if not hasattr(IOLoop, "_instance"):
            with IOLoop._instance_lock:
                if not hasattr(IOLoop, "_instance"):
                    # New instance after double check
                    IOLoop._instance = IOLoop()
        return IOLoop._instance

很顯然,這里是實(shí)現(xiàn)了一個(gè)全局的單例模式。確保多個(gè)線程也只有一個(gè)IOLoop實(shí)例。(思考一下:為什要double check?if not hasattr(IOLoop, "_instance") ^_^)

1.3.3 實(shí)現(xiàn)epoll的接口(假設(shè)是在Linux平臺(tái))

我們知道epoll支持3種操作:

EPOLL_CTL_ADD    添加一個(gè)新的epoll事件
EPOLL_CTL_DEL    刪除一個(gè)epoll事件
EPOLL_CTL_MOD    改變一個(gè)事件的監(jiān)聽(tīng)方式

分別對(duì)應(yīng)tornado.IOLoop里面的三個(gè)函數(shù):add_handler, remove_handler, update_handler

下面看看這三個(gè)函數(shù):

    def add_handler(self, fd, handler, events):
        fd, obj = self.split_fd(fd)
        self._handlers[fd] = (obj, stack_context.wrap(handler))
        self._impl.register(fd, events | self.ERROR)

    def update_handler(self, fd, events):
        fd, obj = self.split_fd(fd)
        self._impl.modify(fd, events | self.ERROR)

    def remove_handler(self, fd):
        fd, obj = self.split_fd(fd)
        self._handlers.pop(fd, None)
        self._events.pop(fd, None)
        try:
            self._impl.unregister(fd)
        except Exception:
            gen_log.debug("Error deleting fd from IOLoop", exc_info=True)

這里的self._impl就是select.epoll(),使用方法可以參考epoll接口。

1.3.4 事件驅(qū)動(dòng)模型的大致思路

IOLoop的start()方法用于啟動(dòng)事件循環(huán)(Event Loop)。

(部分源碼)
while self._events:
    fd, events = self._events.popitem()
    try:
        fd_obj, handler_func = self._handlers[fd]
        handler_func(fd_obj, events)
    except (OSError, IOError) as e:
        if errno_from_exception(e) == errno.EPIPE:
            # Happens when the client closes the connection
            pass
        else:
            self.handle_callback_exception(self._handlers.get(fd))
    except Exception:
        self.handle_callback_exception(self._handlers.get(fd))

大致的思路是:有連接進(jìn)來(lái)(client端請(qǐng)求),就丟給epoll,順便注冊(cè)一個(gè)事件和一個(gè)回調(diào)函數(shù),我們主線程還是繼續(xù)監(jiān)聽(tīng)請(qǐng)求;然后在事件循環(huán)中,如果發(fā)生了某種事件(如socket可讀,或可寫(xiě)),則調(diào)用之前注冊(cè)的回調(diào)函數(shù)去處理。這和Node.js的思路是一致的。

1.3.5 關(guān)于cpu bound任務(wù)

tornado很適合處理IO bound的任務(wù),如果遇到cpu bound的任務(wù),則還是會(huì)阻塞整個(gè)進(jìn)程。這個(gè)時(shí)候就必須將耗時(shí)的任務(wù)丟到另一個(gè)worker,或者隊(duì)列中去處理(如celery)。

1.3.6 其他

IOLoop類還有其他一些方法,多為輔助函數(shù),讀者可以自行參考,此處不詳述。

行文比較草率,如有錯(cuò)誤和不足之處,敬請(qǐng)指正。

下次繼續(xù)分析tornado其他模塊。^_^

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/38118.html

相關(guān)文章

  • tornado 源碼閱讀-初步認(rèn)識(shí)

    摘要:序言最近閑暇無(wú)事閱讀了一下的源碼對(duì)整體的結(jié)構(gòu)有了初步認(rèn)識(shí)與大家分享不知道為什么右邊的目錄一直出不來(lái)非常不舒服不如移步到吧是的核心模塊也是個(gè)調(diào)度模塊各種異步事件都是由他調(diào)度的所以必須弄清他的執(zhí)行邏輯源碼分析而的核心部分則是這個(gè)循環(huán)內(nèi)部的邏輯貼 序言 最近閑暇無(wú)事,閱讀了一下tornado的源碼,對(duì)整體的結(jié)構(gòu)有了初步認(rèn)識(shí),與大家分享 不知道為什么右邊的目錄一直出不來(lái),非常不舒服. 不如移...

    2450184176 評(píng)論0 收藏0
  • tornado 源碼 coroutine 分析

    摘要:源碼之分析的協(xié)程原理分析版本為支持異步,實(shí)現(xiàn)了一個(gè)協(xié)程庫(kù)。提供了回調(diào)函數(shù)注冊(cè)當(dāng)異步事件完成后,調(diào)用注冊(cè)的回調(diào)中間結(jié)果保存結(jié)束結(jié)果返回等功能注冊(cè)回調(diào)函數(shù),當(dāng)被解決時(shí),改回調(diào)函數(shù)被調(diào)用。相當(dāng)于喚醒已經(jīng)處于狀態(tài)的父協(xié)程,通過(guò)回調(diào)函數(shù),再執(zhí)行。 tornado 源碼之 coroutine 分析 tornado 的協(xié)程原理分析 版本:4.3.0 為支持異步,tornado 實(shí)現(xiàn)了一個(gè)協(xié)程庫(kù)。 ...

    NicolasHe 評(píng)論0 收藏0
  • tornado 源碼分析 異步io的實(shí)現(xiàn)方式

    摘要:前言本文將嘗試詳細(xì)的帶大家一步步走完一個(gè)異步操作從而了解是如何實(shí)現(xiàn)異步的其實(shí)本文是對(duì)上一篇文的實(shí)踐和復(fù)習(xí)主旨在于關(guān)注異步的實(shí)現(xiàn)所以會(huì)忽略掉代碼中的一些異常處理文字較多湊合下吧接下來(lái)只會(huì)貼出部分源碼幫助理解希望有耐心的同學(xué)打開(kāi)源碼一起跟蹤一遍 前言 本文將嘗試詳細(xì)的帶大家一步步走完一個(gè)異步操作,從而了解tornado是如何實(shí)現(xiàn)異步io的. 其實(shí)本文是對(duì)[上一篇文][1]的實(shí)踐和復(fù)習(xí) 主...

    xiangzhihong 評(píng)論0 收藏0
  • Python:Tornado 第二章:實(shí)戰(zhàn)演練:開(kāi)發(fā)Tornado網(wǎng)站:第七節(jié):安全Cookie機(jī)制

    摘要:上一篇文章第二章實(shí)戰(zhàn)演練開(kāi)發(fā)網(wǎng)站第六節(jié)異步與協(xié)程化下一篇文章第二章實(shí)戰(zhàn)演練開(kāi)發(fā)網(wǎng)站第八節(jié)用戶身份認(rèn)證是很多網(wǎng)站為了辨別用戶的身份而存儲(chǔ)在用戶本地終端的數(shù)據(jù),在中使用可以方便地對(duì)進(jìn)行讀寫(xiě)。 上一篇文章:Python:Tornado 第二章:實(shí)戰(zhàn)演練:開(kāi)發(fā)Tornado網(wǎng)站:第六節(jié):異步與協(xié)程化下一篇文章:Python:Tornado 第二章:實(shí)戰(zhàn)演練:開(kāi)發(fā)Tornado網(wǎng)站:第八節(jié):用戶...

    dmlllll 評(píng)論0 收藏0
  • Python:Tornado 第二章:實(shí)戰(zhàn)演練:開(kāi)發(fā)Tornado網(wǎng)站:第一節(jié):網(wǎng)站結(jié)構(gòu):Hello

    摘要:上一篇文章第一章異步及協(xié)程基礎(chǔ)第三節(jié)協(xié)程下一篇文章第二章實(shí)戰(zhàn)演練開(kāi)發(fā)網(wǎng)站第二節(jié)網(wǎng)站結(jié)構(gòu)路由解析實(shí)例瀏覽器輸入鏈接頁(yè)面顯示下面逐行解析上面的代碼做了些什么首先通過(guò)語(yǔ)句引入包中的和類。該對(duì)象的第一個(gè)餐食用于定義程序的路由映射。 上一篇文章:Python:Tornado 第一章:異步及協(xié)程基礎(chǔ):第三節(jié):協(xié)程下一篇文章:Python:Tornado 第二章:實(shí)戰(zhàn)演練:開(kāi)發(fā)Tornado網(wǎng)站:第...

    Taonce 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Lsnsh

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<