loadlj / blog

17 stars 6 forks source link

Tornado之web.py #7

Open loadlj opened 7 years ago

loadlj commented 7 years ago

web.py

Tornado的web框架核心文件

Application

A collection of request handlers that make up a web application 主要是用来定义URI路由和对应的处理的Handler.

函数说明

init:

接受一个setting字典参数,里边包括gzip,ui_modules,ui_methods,static_path等一系列参数。并调用_load_ui_modules和_load_ui_methods去加载一些ui资源。此外还会调用self.add_handlers去加载handlers.

add_handlers:

self.handlers.append((re.compile(host_pattern), handlers))

这里的host_pattern为.*?.

spec = URLSpec(pattern, handler, kwargs)
handlers.append(spec)

将regex和相应的handler_class封装URLSpec对象,将其添加到handlers中.

call:

Called by HTTPServer to execute the request 当有新的请求传进来的时候,调用这个方法.

handlers = self._get_host_handlers(request)

获取请求host的handlers list.

handler = spec.handler_class(self, request, **spec.kwargs)

将handler初始化为RequestHandler

handler._execute(transforms, *args, **kwargs)

RequestHandler调用_execute.

RequestHandler

Subclass this class and define get() or post() to make a handler

支持的http方法有"GET", "HEAD", "POST", "DELETE", "PUT"这几种

函数说明

init:

声明self.application和self.request,调用self.clear方法

_execute:

if self.request.method not in self.SUPPORTED_METHODS:
    return HTTPError(405)

如果请求方法不在定义之内,返回405错误。调用self.prepare()方法,这个方法是在handler之前调用的。

getattr(self, self.request.method.lower())(*args, **kwargs)

调用handler中复写的http方法,最后调用finish方法。

write

如果传入的是字典,encode为json对象

chunk = escape.json_encode(chunk)

将需要返回的chunk内容加入到self._write_buffer中。

finish

判断status_code和etag以及其他一些列参数。如果http request中带有connection,将绑定在IOStream上的callback清除即可

self.request.connection.stream.set_close_callback(None)

调用self.flush方法,调用self.request.finish()。

flush

将self._write_buffer拼接成字符串

chunk = "".join(self._write_buffer)

判断reuqest请求方法, 调用requests的write方法:

self.request.write(headers)

通过代码分析

#!/usr/bin/env python
# -*- encoding:utf-8 -*-

from tornado import web, httpserver, ioloop

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

if __name__ == "__main__":
    application = web.Application([
        (r"/", MainHandler),
    ])
    http_server = httpserver.HTTPServer(application)
    http_server.listen(8888)
    ioloop.IOLoop.instance().start()

流程图

流程图

异步模块asynchronous

代码很简短,直接贴上来

def asynchronous(method):
    """Wrap request handler methods with this if they are asynchronous.

    If this decorator is given, the response is not finished when the
    method returns. It is up to the request handler to call self.finish()
    to finish the HTTP request. Without this decorator, the request is
    automatically finished when the get() or post() method returns.

       class MyRequestHandler(web.RequestHandler):
           @web.asynchronous
           def get(self):
              http = httpclient.AsyncHTTPClient()
              http.fetch("http://friendfeed.com/", self._on_download)

           def _on_download(self, response):
              self.write("Downloaded!")
              self.finish()

    """

    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        if self.application._wsgi:
            raise Exception("@asynchronous is not supported for WSGI apps")
        self._auto_finish = False
        return method(self, *args, **kwargs)
    return wrapper

这里文档也说的很清楚,如果没有加装饰器,当get或者post方法返回时,http requests就直接执行并返回。当加上装饰器后,就需要结合上面的_execute方法来进行判断了,这里关键就是加上了一行self._auto_finish = False。

if not self._finished:
    getattr(self, self.request.method.lower())(*args, **kwargs)
       if self._auto_finish and not self._finished:
           self.finish()

这里有一个判断,当self._auto_finish为False的时候,是不执行self.finish()的,所以这里的这个连接会一直打开,除非我们自己去调用self.finish()这个方法。 这里的finish方法前面没有细写,这里剖析一下。finish方法调用self.request.finish(), 跟进到request里面看,requests调用self._finish_request(), 最后执行

self.stream.read_until("\r\n\r\n", self._on_headers)

这里就是将这个socket的文件描述符重新设置为读事件, 就是初始化到一开始的状态。 用例子说明一下这里支持异步的真正含义。

假设我们要去请求十个url,并将其结果返回给客户端,同步的写法是这样:

sync mode
class MainHandler(web.RequestHandler):
    def get(self):
        client = httpclient.HTTPClient()
        for _ in xrange(10):
            response = client.fetch("http://example.com")
            self.write(response.body)
        self.finish()

如果我们不加这个asynchronous装饰器的话,直接用AsyncHTTPClient去请求的话,代码如下:

eror async mode
class MainHandler(web.RequestHandler):
    def get(self):
        done_list = []
        client = httpclient.AsyncHTTPClient()
        for i in xrange(10):
            response = client.fetch("http://example.com", complete)
        def complete(response):
            self.write(response.body)
            done_list.append(response)
            if len(done_list == 10):
                self.finish()

这里会报错assert not self._finished,如果不加装饰器,使用异步lib的进行write的话,当回调完成后,连接却关闭了,无法继续write。

正确的写法应该是:

correct async mode
@web.asynchronous
class MainHandler(web.RequestHandler):
    def get(self):
        done_list = []
        client = httpclient.AsyncHTTPClient()
        for i in xrange(10):
            response = client.fetch("http://example.com", complete)
        def complete(response):
            self.write(response.body)
            done_list.append(response)
            if len(done_list == 10):
                self.finish()

由于这里的代码是异步的,不会直接返回,所以http method不会阻塞整个ioloop,不影响其他的连接。