`

Tornado ioloop源码简析

阅读更多
#!/usr/bin/env python
#-*-encoding:utf-8-*-

from tornado import httpserver
from tornado import ioloop

def handle_request(request):
   message = "You requested %s\n" % request.uri
   request.write("HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s" % (
                 len(message), message))
   request.finish()
http_server = httpserver.HTTPServer(handle_request)#在此打断点,来看下httpserver运行的过程。
http_server.listen(8888)
ioloop.IOLoop.instance().start()

http_server = httpserver.HTTPServer(handle_request)
依然使用httpserver那篇的例子,继续调试ioloop的相关部分。
def add_sockets(self, sockets):

        if self.io_loop is None:
            self.io_loop = IOLoop.current()

        for sock in sockets:
            self._sockets[sock.fileno()] = sock
            add_accept_handler(sock, self._handle_connection,
                               io_loop=self.io_loop)

    @staticmethod
    def current():
        #获取当前IOLoop是否存在实例,如果没有则初始化
        current = getattr(IOLoop._current, "instance", None)       
        if current is None:
            return IOLoop.instance()
        return current

    #单例模式,确保IOLoop仅被实例化一次。
    @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


    with IOLoop._instance_lock——>_instance_lock = threading.Lock()
   
    @classmethod
    def configurable_default(cls):
        if hasattr(select, "epoll"):
            from tornado.platform.epoll import EPollIOLoop
            return EPollIOLoop
        if hasattr(select, "queue"):
            # Python 2.6+ on BSD or Mac
            from tornado.platform.kqueue import KQueueIOLoop
            return KQueueIOLoop
        from tornado.platform.select import SelectIOLoop
        return SelectIOLoop

判断支持哪种io复用模型,Linux epoll,BSD以及MacOS kqueue,否则返回select模型。
   
引用

    For concrete implementations, see `tornado.platform.epoll.EPollIOLoop`
    (Linux), `tornado.platform.kqueue.KQueueIOLoop` (BSD and Mac), or
    `tornado.platform.select.SelectIOLoop` (all platforms)
  

由于当前系统为MacOS所以接下来跟踪的是KQueue
class KQueueIOLoop(PollIOLoop):
       #KQueueIOLoop继承的是PollIOLoop。
       def initialize(self, **kwargs):
             super(KQueueIOLoop, self).initialize(impl=_KQueue(), **kwargs)
   

然后ioloop.IOLoop.instance().start() 开始事件循环
 
  def start(self):
        if not logging.getLogger().handlers:
            # The IOLoop catches and logs exceptions, so it's
            # important that log output be visible.  However, python's
            # default behavior for non-root loggers (prior to python
            # 3.2) is to print an unhelpful "no handlers could be
            # found" message rather than the actual log entry, so we
            # must explicitly configure logging if we've made it this
            # far without anything.
            logging.basicConfig()
        if self._stopped:
            self._stopped = False
            return
        old_current = getattr(IOLoop._current, "instance", None)
        IOLoop._current.instance = self
        self._thread_ident = thread.get_ident()
        self._running = True

        # signal.set_wakeup_fd closes a race condition in event loops:
        # a signal may arrive at the beginning of select/poll/etc
        # before it goes into its interruptible sleep, so the signal
        # will be consumed without waking the select.  The solution is
        # for the (C, synchronous) signal handler to write to a pipe,
        # which will then be seen by select.
        #
        # In python's signal handling semantics, this only matters on the
        # main thread (fortunately, set_wakeup_fd only works on the main
        # thread and will raise a ValueError otherwise).
        #
        # If someone has already set a wakeup fd, we don't want to
        # disturb it.  This is an issue for twisted, which does its
        # SIGCHILD processing in response to its own wakeup fd being
        # written to.  As long as the wakeup fd is registered on the IOLoop,
        # the loop will still wake up and everything should work.
        old_wakeup_fd = None
        if hasattr(signal, 'set_wakeup_fd') and os.name == 'posix':
            # requires python 2.6+, unix.  set_wakeup_fd exists but crashes
            # the python process on windows.
            try:
                old_wakeup_fd = signal.set_wakeup_fd(self._waker.write_fileno())
                if old_wakeup_fd != -1:
                    # Already set, restore previous value.  This is a little racy,
                    # but there's no clean get_wakeup_fd and in real use the
                    # IOLoop is just started once at the beginning.
                    signal.set_wakeup_fd(old_wakeup_fd)
                    old_wakeup_fd = None
            except ValueError:  # non-main thread
                pass

        while True:
            poll_timeout = 3600.0

            # Prevent IO event starvation by delaying new callbacks
            # to the next iteration of the event loop.
            with self._callback_lock:
                callbacks = self._callbacks
                self._callbacks = []
            for callback in callbacks:
                self._run_callback(callback)

            if self._timeouts:
                now = self.time()
                while self._timeouts:
                    if self._timeouts[0].callback is None:
                        # the timeout was cancelled
                        heapq.heappop(self._timeouts)
                    elif self._timeouts[0].deadline <= now:
                        timeout = heapq.heappop(self._timeouts)
                        self._run_callback(timeout.callback)
                    else:
                        seconds = self._timeouts[0].deadline - now
                        poll_timeout = min(seconds, poll_timeout)
                        break

            if self._callbacks:
                # If any callbacks or timeouts called add_callback,
                # we don't want to wait in poll() before we run them.
                poll_timeout = 0.0

            if not self._running:
                break

            if self._blocking_signal_threshold is not None:
                # clear alarm so it doesn't fire while poll is waiting for
                # events.
                signal.setitimer(signal.ITIMER_REAL, 0, 0)

            try:
                event_pairs = self._impl.poll(poll_timeout)
            except Exception as e:
                # Depending on python version and IOLoop implementation,
                # different exception types may be thrown and there are
                # two ways EINTR might be signaled:
                # * e.errno == errno.EINTR
                # * e.args is like (errno.EINTR, 'Interrupted system call')
                if (getattr(e, 'errno', None) == errno.EINTR or
                    (isinstance(getattr(e, 'args', None), tuple) and
                     len(e.args) == 2 and e.args[0] == errno.EINTR)):
                    continue
                else:
                    raise

            if self._blocking_signal_threshold is not None:
                signal.setitimer(signal.ITIMER_REAL,
                                 self._blocking_signal_threshold, 0)

            # Pop one fd at a time from the set of pending fds and run
            # its handler. Since that handler may perform actions on
            # other file descriptors, there may be reentrant calls to
            # this IOLoop that update self._events
            self._events.update(event_pairs)
            while self._events:
                fd, events = self._events.popitem()
                try:
                    self._handlers[fd](fd, events)
                except (OSError, IOError) as e:
                    if e.args[0] == errno.EPIPE:
                        # Happens when the client closes the connection
                        pass
                    else:
                        app_log.error("Exception in I/O handler for fd %s",
                                      fd, exc_info=True)
                except Exception:
                    app_log.error("Exception in I/O handler for fd %s",
                                  fd, exc_info=True)
        # reset the stopped flag so another start/stop pair can be issued
        self._stopped = False
        if self._blocking_signal_threshold is not None:
            signal.setitimer(signal.ITIMER_REAL, 0, 0)
        IOLoop._current.instance = old_current
        if old_wakeup_fd is not None:
            signal.set_wakeup_fd(old_wakeup_fd)

   有关KQueue以及Epoll等陆续会单独整理总结。
   打开http://127.0.0.1:8888/ 调试进入iostream。
Others:
1、errno
系统符号
引用
This module makes available standard errno system symbols. The value of each symbol is the corresponding integer value. The names and descriptions are borrowed from linux/include/errno.h, which should be pretty all-inclusive.

2、heapq
3、select
引用
This module provides access to the select() and poll() functions available in most operating systems, epoll() available on Linux 2.5+ and kqueue() available on most BSD. Note that on Windows, it only works for sockets; on other operating systems, it also works for other file types (in particular, on Unix, it works on pipes). It cannot be used on regular files to determine whether a file has grown since it was last read.

4、signal待整理
5、socket listen backlog参数以及setblocking()
6、getattr和setattr以及hasattr
Question:使用getattr的好处?
http://docs.python.org/2/library/functions.html#hasattr
http://docs.python.org/2/library/functions.html#getattr
http://docs.python.org/2/library/functions.html#setattr

参考资料:
http://docs.python.org/2/library/threading.html#lock-objects
http://effbot.org/zone/python-with-statement.htm
http://docs.python.org/2/reference/compound_stmts.html#the-with-statement 
http://docs.python.org/2/library/signal.html
http://www.tornadoweb.org/documentation/ioloop.html
http://amyangfei.me/2013/01/27/tornado-source-analysis-1/
http://docs.python.org/2/library/select.html?highlight=select
http://stackoverflow.com/questions/7072701/whats-the-tornado-ioloop-and-tornados-workflow
http://golubenco.org/2009/09/19/understanding-the-code-inside-tornado-the-asynchronous-web-server-powering-friendfeed/
分享到:
评论

相关推荐

    tornado-1.2.0源码

    《深入剖析Tornado 1.2.0源码:揭示Web服务器的秘密》 Tornado,一个轻量级、高性能的Python Web服务器框架,以其异步非阻塞I/O模式著称,尤其适用于处理大量并发连接。本文将深入探讨Tornado 1.2.0版本的源码,...

    tornado框架短租web系统源码

    tornado作为用户产品后台服务器核心框架 redis 保存session数据、短时间房源信息、地域信息等,保存页面缓存数据,提高服务器响应速度 采用前后端完全分离架构,采用ajax异步调用、json数据传输,使后端接口可以...

    python+tornado开发的实例源码

    在"python+tornado开发的实例源码"中,我们可以学习到以下几个关键知识点: 1. **Python基础**:Python是一种高级编程语言,以其简洁明了的语法和丰富的标准库而闻名。在Tornado应用中,你需要了解Python的基本语法...

    tornado 多进程模式解析

    本文主要研究的是tornado 多进程模式的相关... tornado.ioloop.IOLoop.instance().start() 并且在listen中,将tornado启动为单进程模型。 所以要启动为多进程模型的方法为:摒弃listen方法 http_server = tornado.ht

    Tornado源码分析之http服务器篇

    8. **源码调试技巧**:介绍如何阅读和理解Tornado源码,以及使用Python调试工具进行源码调试的方法。 总之,这篇文章对于想深入了解Tornado框架,特别是其HTTP服务器实现的开发者来说,是一份宝贵的资源。通过源码...

    tornado源代码

    - **tornado**: 包含 Tornado 源码的核心模块,如 httpserver.py(HTTP 服务器实现)、websocket.py(WebSocket 支持)、ioloop.py(I/O 循环)等。 - **test**: 测试目录,包含了对 Tornado 各个组件的单元测试,...

    Tornado教程.pdf

    tornado.ioloop.IOLoop.current().start() ``` 在这个例子中,`IOLoop.current().start()` 启动了事件循环,等待处理到来的请求。 **三、Tornado 的长轮询与 WebSockets** Tornado 在实时 Web 应用中的优势在于...

    tornado官方翻译文档

    Tornado官方翻译文档是...然后我们创建了一个应用实例,并将端口设置为监听8888,接着使用tornado.ioloop.IOLoop.current().start()启动了应用。这个例子尽管简单,但是体现了Tornado Web框架的基本结构和运行机制。

    tornado入门大全

    tornado.ioloop.IOLoop.current().start() ``` 这个例子定义了一个处理GET请求的处理器`MainHandler`,当访问根路径"/"时,返回"Hello, world"。 ### 四、Tornado的路由系统 Tornado的路由系统使用正则表达式来...

    learning-tornado-src:Tornado框架源码学习笔记

    tornado作为web框架和异步网络库,代码量过多,因此在分析tornado源码时,可以选择一些比较重要的模块来阅读,方式:current.py,gen.py,tcpserver.py,httpserver.py,ioloop .py,iostream.py,web.py等 ...

    python基于Tornado实现,系统核心调度,可分布式扩展

    Tornado采用了一个事件驱动的设计,利用了Python的`asyncio`库(在Python 3中)或者`tornado.ioloop`(在Python 2中)。IOLoop是Tornado的核心,它是一个事件循环,监控和调度所有异步操作。你可以注册回调函数,当...

    股票分析系统源码,基于python,tornado框架.zip

    这个特定的源码是基于Python编程语言实现的,利用了Tornado框架,一个轻量级且高效的异步网络库。下面将详细介绍这个系统可能包含的关键知识点和相关技术。 首先,Python是当今最流行的编程语言之一,尤其在数据...

    tornado 4.0.1 python framework guide

    `tornado.ioloop` 模块是 Tornado 的核心组件之一,它实现了事件驱动模型,负责调度各种 I/O 操作。开发者可以通过 `IOLoop.current().start()` 方法启动事件循环,也可以使用 `IOLoop.current().add_callback()` ...

    tornado 安装包

    tornado.ioloop.IOLoop.current().start() ``` 这个例子中,我们定义了一个名为MainHandler的处理类,它响应GET请求并返回"Hello, world"。然后,我们创建一个Application实例,设置路由,最后启动服务器监听8888...

    tornado实战之一

    from tornado.ioloop import IOLoop class MainHandler(RequestHandler): def get(self): self.write("Hello, Tornado!") if __name__ == "__main__": app = Application([ (r"/", MainHandler), ]) app....

    tornado-1.2.1 python

    tornado.ioloop.IOLoop.current().start() ``` 在这个例子中,我们创建了一个简单的Web服务器,监听8888端口,当访问根URL("/")时返回"Hello, world"。 总的来说,Tornado 1.2.1是一个强大且灵活的工具,适合...

    tornado 2.2.2 source code

    《Tornado 2.2.2内核源码解析》 Tornado 2.2.2是VxWorks操作系统的一个重要版本,对于嵌入式开发人员来说,深入理解其内核源码对于优化系统性能、解决运行时问题以及进行定制化开发具有极大的价值。本文将对Tornado...

    tornado docs.pdf

    示例中没有使用Tornado的异步特性,但通过一个简单的“Hello, world”页面,演示了如何使用Tornado的IOLoop和Web模块来创建Web应用程序。开发者可以将此示例作为入门Tornado Web开发的起点。 文档还提到,与其他...

    tornado.pdf

    tornado.ioloop.IOLoop.current().start() ``` 这段代码定义了一个名为`MainHandler`的请求处理器,并将其绑定到根URL("/")。当收到GET请求时,它会返回"Hello, world"。 3. **WSGI兼容性**:虽然Tornado有自己...

    tornado参考1

    Tornado 的安装可以通过 pip 完成,也可以手动下载源码包编译安装。在使用前,开发者需要理解 Tornado 的工作原理,主要是基于它的异步事件驱动模型,其中 ioloop(I/O 循环)是核心,它负责监听和调度各种 I/O 事件...

Global site tag (gtag.js) - Google Analytics