tornado源码分析-Application

tornado.web包含web框架的大部分主要功能,Application是其中一个重要的类

Application类的作用是实现 URI 转发,将 Application 的实例传递给 httpserver ,当监听到请求时,把服务器传回来的请求进行转发,通过调用 __call__ ,处理请求。

 

Application源码:

class Application(httputil.HTTPServerConnectionDelegate):
    """A collection of request handlers that make up a web application.

    Instances of this class are callable and can be passed directly to
    HTTPServer to serve the application::

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

    The constructor for this class takes in a list of `URLSpec` objects
    or (regexp, request_class) tuples. When we receive requests, we
    iterate over the list in order and instantiate an instance of the
    first request class whose regexp matches the request path.
    The request class can be specified as either a class object or a
    (fully-qualified) name.

    Each tuple can contain additional elements, which correspond to the
    arguments to the `URLSpec` constructor.  (Prior to Tornado 3.2, this
    only tuples of two or three elements were allowed).

    A dictionary may be passed as the third element of the tuple,
    which will be used as keyword arguments to the handler's
    constructor and `~RequestHandler.initialize` method.  This pattern
    is used for the `StaticFileHandler` in this example (note that a
    `StaticFileHandler` can be installed automatically with the
    static_path setting described below)::

        application = web.Application([
            (r"/static/(.*)", web.StaticFileHandler, {"path": "/var/www"}),
        ])

    We support virtual hosts with the `add_handlers` method, which takes in
    a host regular expression as the first argument::

        application.add_handlers(r"www\.myhost\.com", [
            (r"/article/([0-9]+)", ArticleHandler),
        ])

    You can serve static files by sending the ``static_path`` setting
    as a keyword argument. We will serve those files from the
    ``/static/`` URI (this is configurable with the
    ``static_url_prefix`` setting), and we will serve ``/favicon.ico``
    and ``/robots.txt`` from the same directory.  A custom subclass of
    `StaticFileHandler` can be specified with the
    ``static_handler_class`` setting.

    """
    def __init__(self, handlers=None, default_host="", transforms=None,
                 **settings):
        if transforms is None:
            self.transforms = []
            if settings.get("compress_response") or settings.get("gzip"):
                self.transforms.append(GZipContentEncoding)
        else:
            self.transforms = transforms
        self.handlers = [] 
        self.named_handlers = {}
        self.default_host = default_host
        self.settings = settings
        self.ui_modules = {'linkify': _linkify,
                           'xsrf_form_html': _xsrf_form_html,
                           'Template': TemplateModule,
                           }
        self.ui_methods = {}
        self._load_ui_modules(settings.get("ui_modules", {}))
        self._load_ui_methods(settings.get("ui_methods", {}))
        if self.settings.get("static_path"):
            path = self.settings["static_path"]
            handlers = list(handlers or [])
            static_url_prefix = settings.get("static_url_prefix",
                                             "/static/")
            static_handler_class = settings.get("static_handler_class",
                                                StaticFileHandler)
            static_handler_args = settings.get("static_handler_args", {})
            static_handler_args['path'] = path
            for pattern in [re.escape(static_url_prefix) + r"(.*)",
                            r"/(favicon\.ico)", r"/(robots\.txt)"]:
                handlers.insert(0, (pattern, static_handler_class,
                                    static_handler_args))
        if handlers:
            self.add_handlers(".*$", handlers)

        if self.settings.get('debug'): # 如果debug = True, 开启autoreload 和 serve_traceback功能(出错显示错误信息), 关闭compiled_template_cache和static_hash_cache<span style="font-family: Arial, Helvetica, sans-serif;">功能</span>

            self.settings.setdefault('autoreload', True)
            self.settings.setdefault('compiled_template_cache', False)
            self.settings.setdefault('static_hash_cache', False)
            self.settings.setdefault('serve_traceback', True)

        # Automatically reload modified modules
        if self.settings.get('autoreload'): # 服务能够自动reload新的代码
            from tornado import autoreload
            autoreload.start()

    def listen(self, port, address="", **kwargs):
        """Starts an HTTP server for this application on the given port.

        This is a convenience alias for creating an `.HTTPServer`
        object and calling its listen method.  Keyword arguments not
        supported by `HTTPServer.listen <.TCPServer.listen>` are passed to the
        `.HTTPServer` constructor.  For advanced uses
        (e.g. multi-process mode), do not use this method; create an
        `.HTTPServer` and call its
        `.TCPServer.bind`/`.TCPServer.start` methods directly.

        Note that after calling this method you still need to call
        ``IOLoop.instance().start()`` to start the server.
        接受端口,地址,其它参数
        建立http服务器并监听该端口
        """
        # import is here rather than top level because HTTPServer
        # is not importable on appengine
        from tornado.httpserver import HTTPServer
        server = HTTPServer(self, **kwargs)
        server.listen(port, address)

    def add_handlers(self, host_pattern, host_handlers):
        """Appends the given handlers to our handler list.

        Host patterns are processed sequentially in the order they were
        added. All matching patterns will be considered.
        """
        if not host_pattern.endswith("$"):
            host_pattern += "$"
        handlers = []
        # The handlers with the wildcard host_pattern are a special
        # case - they're added in the constructor but should have lower
        # precedence than the more-precise handlers added later.
        # If a wildcard handler group exists, it should always be last
        # in the list, so insert new groups just before it.
        if self.handlers and self.handlers[-1][0].pattern == '.*$':
            self.handlers.insert(-1, (re.compile(host_pattern), handlers))
        else:
            self.handlers.append((re.compile(host_pattern), handlers))

        for spec in host_handlers:
            if isinstance(spec, (tuple, list)):
                assert len(spec) in (2, 3, 4)
                spec = URLSpec(*spec)
            handlers.append(spec)
            if spec.name:
                if spec.name in self.named_handlers:
                    app_log.warning(
                        "Multiple handlers named %s; replacing previous value",
                        spec.name)
                self.named_handlers[spec.name] = spec

    def add_transform(self, transform_class):
        self.transforms.append(transform_class)

    def _get_host_handlers(self, request):
        host = request.host.lower().split(':')[0]
        matches = []
        for pattern, handlers in self.handlers:
            if pattern.match(host):
                matches.extend(handlers)
        # Look for default host if not behind load balancer (for debugging)
        if not matches and "X-Real-Ip" not in request.headers:
            for pattern, handlers in self.handlers:
                if pattern.match(self.default_host):
                    matches.extend(handlers)
        return matches or None

    def _load_ui_methods(self, methods):
        if isinstance(methods, types.ModuleType):
            self._load_ui_methods(dict((n, getattr(methods, n))
                                       for n in dir(methods)))
        elif isinstance(methods, list):
            for m in methods:
                self._load_ui_methods(m)
        else:
            for name, fn in methods.items():
                if not name.startswith("_") and hasattr(fn, "__call__")                         and name[0].lower() == name[0]:
                    self.ui_methods[name] = fn

    def _load_ui_modules(self, modules):
        if isinstance(modules, types.ModuleType):
            self._load_ui_modules(dict((n, getattr(modules, n))
                                       for n in dir(modules)))
        elif isinstance(modules, list):
            for m in modules:
                self._load_ui_modules(m)
        else:
            assert isinstance(modules, dict)
            for name, cls in modules.items():
                try:
                    if issubclass(cls, UIModule):
                        self.ui_modules[name] = cls
                except TypeError:
                    pass

    def start_request(self, connection):
        # Modern HTTPServer interface
        return _RequestDispatcher(self, connection)

    def __call__(self, request):
        # Legacy HTTPServer interface
        dispatcher = _RequestDispatcher(self, None)
        dispatcher.set_request(request)
        return dispatcher.execute()

    def reverse_url(self, name, *args):
        """Returns a URL path for handler named ``name``

        The handler must be added to the application as a named `URLSpec`.

        Args will be substituted for capturing groups in the `URLSpec` regex.
        They will be converted to strings if necessary, encoded as utf8,
        and url-escaped.
        根据name返回匹配的url路径
        """
        if name in self.named_handlers:
            return self.named_handlers[name].reverse(*args)
        raise KeyError("%s not found in named urls" % name)

    def log_request(self, handler):
        """Writes a completed HTTP request to the logs.

        By default writes to the python root logger.  To change
        this behavior either subclass Application and override this method,
        or pass a function in the application settings dictionary as
        ``log_function``.
        把HTTP请求写进日志
        http状态码小于400的为正常
        大于等于400小于500<span style="font-family: Arial, Helvetica, sans-serif;">为</span><span style="font-family: Arial, Helvetica, sans-serif;">请求错误</span>
        大于500是服务器错误
        """
        if "log_function" in self.settings:
            self.settings["log_function"](handler)
            return
        if handler.get_status() < 400:
            log_method = access_log.info
        elif handler.get_status() < 500:
            log_method = access_log.warning
        else:
            log_method = access_log.error
        request_time = 1000.0 * handler.request.request_time()
        log_method("%d %s %.2fms", handler.get_status(),
                   handler._request_summary(), request_time)



构造函数

__init__(self,handlers=None,default_host="",transforms=None,**settings)  

它接受handlers (包含匹配规则和requesthandler的元组),default_host(默认主机),transforms(输出做分块和压缩的转换)和setting(包含配置的字典)。

建立主机的路径路由规则

 

方法

listen(self, port, address="", **kwargs) 建立http服务器并监听该端口

add_handlers(self, host_pattern, host_handlers)向handler列表中添加handler。host_pattern依次按照它们的添加顺序进行处理,添加主机的路径路由规则

add_transform(self, transform_class)向self.transforms增加transform_class,对输出做分块和压缩的转换

_get_host_handlers(self, request)寻找属于这个request的handlers

_load_ui_methods(self, methods)在self.ui_methods中添加方法

_load_ui_modules(self, modules)在self.ui_modules中添加方法

reverse_url(self, name, *args)使用name返回匹配的url路径

__call__(self, request) 在服务器接受新连接时被调用 接受HttpRequest对象 根据 httprequest 调用execute 方法

start_request(self, connection)作用同__call__

log_request(self, handler)把HTTP请求写进日志。


debug=True 时代码动态自动编译的原理

Application 对象实例化时,给出“debug=True”参数的话开启autoreload 和 serve_traceback功能(出错显示错误信息) , 关闭compiled_template_cache和static_hash_cache功能  


autoreload:改变源代码的时候,服务器进程将能够自动reload新的代码并重启。

serve_traceback:如果开启,在出错的时候就不是返回默认的错误页面,取而代之的是显示python的traceback,利用这个我们可以看到在什么地方出现了错误,可以进行方便的调试

compiled_template_cache:编译模板缓存,如果关闭,在刷新时服务器的模板都会重新加载

static_hash_cache:静态哈希缓存如果关闭,在刷新时有static_url()的地方都会重新载入需要读取的static下的文件,而不是用已经缓存了的文件

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。