Tornado源码阅读

这几天看了Tornado的源码,写这篇文章以做总结。本文采用Tornado v1.2版本的源码,讨论Tornado运行过程而不沉浸入代码实现。

主要模块分析

|---web.py (应用框架层)
|---httpserver.py ( HTTP TCP层 )
|---ioloop.py (数据处理层)
|---iostream.py

web.py 实现了tornado的web框架,定义了Application, RequestHandler两个类。Application是一个单例,注册了全局路由,服务器转发过来的请求调用该类的__call__()。RequestHandler主要功能主要是将handler和url进行映射。

httpserver.py 建立http服务器,并解析http请求。

ioloop.py 主要是将底层的epoll或者说是其他的IO多路复用封装作异步事件来处理。

iostream.py 对sock进行封装。

Tornado运行流程:从请求到响应

从main函数出发:

application = web.Application([
    (r"/", MainPageHandler),
])
http_server = httpserver.HTTPServer(application)
http_server.listen(8080)
ioloop.IOLoop.instance().start()

首先实例化Application(),并设置了路由表。

将application实例传入HTTPServer

    # httpserver.HTTPServer初始化
    def __init__(self, request_callback, no_keep_alive=False...):
       # request_callback 便是传入的application
        self.request_callback = request_callback
        ...

接着http_server监听8080端口,listen()先绑定了端口和地址(self.bind),接着是一系列的判断之后调用self.start()

   # start()函数最重要的一件事就是将self._sockets加入ioloop
    def start(self, num_processes=1):
         if ...
                    ...        
                    self.io_loop = ioloop.IOLoop.instance()
                    # 当有对应请求来时,ioloop将当前线程拉起,  
                    # 执行回调函数 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)

接下来看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)
                # 实例化HTTPConnection,回调函数self.request_callback
                # 便是application,HTTPConnection实现对请求的解析
                HTTPConnection(stream, address, self.request_callback,
                               self.no_keep_alive, self.xheaders)
            ...

这里需要注意一下HTTPConnection初始化过程

    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()实现对请求解析出请求头然后组装成HTTPRequest,最后将组装后的请求使用request_callback回调

    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])
            ...
            # 在这里调用了application
            self.request_callback(self._request)
        ...

所以我们便回到了__call__(),看看__call__()干了什么

    def __call__(self, request):
        ...
        # 获取handlers,根据官方文档,如果有多个,取第一个
        handlers = self._get_host_handlers(request)  
        ...
        handler = spec.handler_class(self, request, **spec.kwargs)  # 找到handler
        handler._execute(transforms, *args, **kwargs)  # 执行
        return handler

到了这里,便是用_execute执行handler的业务逻辑,那么便到此结束了吗?答案是否定的,我们还忘了一个最重要的ioloop,其实我们在http_server.start()便使用到ioloop了,下面看看ioloop是怎么轮询的

        while True:
            ...
            try:
                # 这里开始循环轮询,具体干了什么我也不知道
                event_pairs = self._impl.poll(poll_timeout)
           ...
           self._events.update(event_pairs)
           while self._events:
                fd, events = self._events.popitem()
                try:
                    # 这里才是重点,经过前面的铺垫我们有了一个events,接着便是调用对应  
                    # 的handlers,当然我们的这个handlers不是平白无故跳出来的,还记得  
                    # start()最重要的功能是什么吗?
                    self._handlers[fd](fd, events)

到了这里,对于Tornado从一个请求到一个响应的整个过程涉及到的主内容基本过了一遍。在程序初始化阶段,application完成路由表的注册,httpserver建立、绑定端口并开始监听,同时将sockets注册到ioloop中,程序开始后,ioloop不断轮询,当检测到有请求后,通过其文件描述符(sock)找到对应的handlers,此时将执行注册时的回调函数_handle_events(),解析headers,重新封装httprequire后传给application,application根据request找到对应的handler,并执行响应的业务逻辑,之后便是生成相应的response。放图一张(源自:)


Tornado源码阅读_第1张图片

总结

从一开始的同步、异步、阻塞、非阻塞、多路复用等基本概念出发,参考上一篇文章,了解了传统网络模型中的进程模型、线程模型,当访问人数越来越多时,传统的网络模型,一个进程或一个线程只能处理一个链接便不再适用。于是提出了多路复用的概念,tornado主要讨论了epoll的使用,同时,tornado适用iostream实现异步读写,以达到高并发、高性能的目的。

相信随着学习的深入,对这篇文章的理解以及修改将一直进行下去。

你可能感兴趣的:(Tornado源码阅读)