首页 > 其他 > 详细

笔记-flask-原理及请求处理流程

时间:2019-03-24 14:38:35      阅读:92      评论:0      收藏:0      [点我收藏+]

笔记-flask-原理及请求处理流程

 

1.      服务器声明及运行

最基本的flask项目代码如下

from flask import Flask

app = Flask(__name__)

 

@app.route("/")

def hello():

    return "Hello World!"

 

if __name__ == "__main__":

    app.run()

 

简单来说,声明Flask对象,声明view function,运行flask对象。

看一下后台做了什么。

1.1.    __init__

看一下Flask对象的__init__方法:

         #: Holds the path to the instance folder.
        #: .. versionadded:: 0.8
       
self.instance_path = instance_path

        #: The configuration dictionary as :class:`Config`.  This behaves
        #: exactly like a regular dictionary but supports additional methods
        #: to load a config from files.
       
self.config = self.make_config(instance_relative_config)

        #: A dictionary of all view functions registered.
       
self.view_functions = {}

        #: A dictionary of all registered error handlers.
       
self.error_handler_spec = {}

        #: A list of functions that are called when:meth:`url_for`raises a
        #: :exc:`~werkzeug.routing.BuildError
       
self.url_build_error_handlers = []

        #: A dictionary with lists of functions that will be called at the
        #: beginning of each request.

         #use the :meth:`before_request`: decorator.
       
self.before_request_funcs = {}

        #: A list of functions that will be called at the beginning of the
        #: first request to this instance.
        #: :meth:`before_first_request` decorator.
       
self.before_first_request_funcs = []

        #: A dictionary with lists of functions that should be called after
        #: each request. 

        #: :meth:`after_request` decorator.
       
self.after_request_funcs = {}

        #: A dictionary with lists of functions that are called after
        #: each request, even if an exception has occurred.

        #: :meth:`teardown_request` decorator.
       
self.teardown_request_funcs = {}

        #: A list of functions that are called when the application context
        #: is destroyed.
       
self.teardown_appcontext_funcs = []

        #: A dictionary with lists of functions that are called before the
        #: :attr:`before_request_funcs` functions.
        #: :meth:`url_value_preprocessor`.
       
self.url_value_preprocessors = {}

        #: A dictionary with lists of functions that can be used as URL value
        #: preprocessors.
        #:meth:`url_defaults`
       
self.url_default_functions = {}

        #: A list of shell context processor functions that should be run
        #: when a shell context is created.
       
self.shell_context_processors = []

        #: all the attached blueprints in a dictionary by name.  Blueprints
        #: can be attached multiple times so this dictionary does not tell
        #: you how often they got attached.
        #:
        #: .. versionadded:: 0.7
       
self.blueprints = {}
        self._blueprint_order = []

 

     #: The :class:`~werkzeug.routing.Map` for this instance.  You can use

        #: this to change the routing converters after the class was created

        #: but before any routes are connected.

        self.url_map = Map()

 

        self.url_map.host_matching = host_matching

        self.subdomain_matching = subdomain_matching

 

 

把路径及其它环境变量设置去掉以后,剩下的基本就这些了。

都是一些函数列表,用于在不同时机处理请求。

view_functions保存视图函数,error_handlers保存的错误处理函数

url_map保存uri到视图函数的映射。

 

1.1.1.   route装饰器

顺带讲一下route装饰器:

def route(self, rule, **options):

        """A decorator that is used to register a view function for a

        given URL rule.  This does the same thing as :meth:`add_url_rule`

        but is intended for decorator usage::

 

            @app.route(‘/‘)

            def index():

                return ‘Hello World‘

 

        """

        def decorator(f):

            endpoint = options.pop(‘endpoint‘, None)

            self.add_url_rule(rule, endpoint, f, **options)

            return f

        return decorator

route装饰器的作用是将视图函数与url的对应联系装入self.url_map。

 

1.2.    app.run()

上面说到的是初始化部分,下面看服务器运行部分,当执行app.run()时:

找到run()方法,它做的事情很少,只是设定的一些参数,然后调用了run_simple方法:

    def run(self, host=None, port=None, debug=None,

            load_dotenv=True, **options):

from werkzeug.serving import run_simple

        try:

            run_simple(host, port, self, **options)

        finally:

            # reset the first request information if the development server

            # reset normally.  This makes it possible to restart the server

            # without reloader and that stuff from an interactive shell.

            self._got_first_request = False

 

1.3.    run_simple()

    def inner():

        try:

            fd = int(os.environ[‘WERKZEUG_SERVER_FD‘])

        except (LookupError, ValueError):

            fd = None

        srv = make_server(hostname, port, application, threaded,

                          processes, request_handler,

                          passthrough_errors, ssl_context,

                          fd=fd)

        if fd is None:

            log_startup(srv.socket)

        srv.serve_forever()

这里只列出了核心部分,前面有一些判断处理语句,按序做了以下工作:

  1. 对debug模式以及静态文件的包装;
  2. ShareDataMiddleware就是一个中间件,这里是起到吧文件转换为服务器可接受的Response形式的作用。
  3. use_reloader 用于决定当app代码改变时是否要重启服务器,若是True,则他会建立一个socket,其中的can_open_by_fd由socket中是否由fromfd特征决定,如果可以就将fd储存在环境变量中以便重启后的复用,socket开始监听,而后就调用run_with_reloader,它也接受了函数inner.
  4. 无论use_reloader是不是True时,都会调用函数内部的inner函数。inner函数内,在环境中WERKZEUG_SERVER_FD这个key储存了可以复用的socket,若没有就设为None,然后就调用函数make_server,这根据参数process和threads选择合适的服务器,取得服务器对象后,就调用方法run_forever,这服务器也就启动了。,werkzeug提供了多种可选的服务器,这里是一个基本的单线程单进程服务器

 

1.4.    make_server()

 

def make_server(host=None, port=None, app=None, threaded=False, processes=1,

                request_handler=None, passthrough_errors=False,

                ssl_context=None, fd=None):

    """Create a new server instance that is either threaded, or forks

    or just processes one request after another.

    """

    if threaded and processes > 1:

        raise ValueError("cannot have a multithreaded and "

                         "multi process server.")

    elif threaded:

        return ThreadedWSGIServer(host, port, app, request_handler,

                                  passthrough_errors, ssl_context, fd=fd)

    elif processes > 1:

        return ForkingWSGIServer(host, port, app, processes, request_handler,

                                 passthrough_errors, ssl_context, fd=fd)

    else:

        return BaseWSGIServer(host, port, app, request_handler,

                              passthrough_errors, ssl_context, fd=fd)

基本上就是根据服务器情况创建一个server instance。

继续,选择BaseWSGIServer()去看一下

1.5.    BaseWSGIServer

 

class BaseWSGIServer(HTTPServer, object):

 

    """Simple single-threaded, single-process WSGI server."""

    multithread = False

    multiprocess = False

    request_queue_size = LISTEN_QUEUE

 

    def __init__(self, host, port, app, handler=None,

                 passthrough_errors=False, ssl_context=None, fd=None):

        if handler is None:

            handler = WSGIRequestHandler

 

        self.address_family = select_ip_version(host, port)

 

        if fd is not None:

            real_sock = socket.fromfd(fd, self.address_family,

                                      socket.SOCK_STREAM)

            port = 0

        HTTPServer.__init__(self, get_sockaddr(host, int(port),

                                               self.address_family), handler)

        self.app = app

        self.passthrough_errors = passthrough_errors

        self.shutdown_signal = False

        self.host = host

        self.port = self.socket.getsockname()[1]

 

        # Patch in the original socket.

        if fd is not None:

            self.socket.close()

            self.socket = real_sock

            self.server_address = self.socket.getsockname()

 

        if ssl_context is not None:

            if isinstance(ssl_context, tuple):

                ssl_context = load_ssl_context(*ssl_context)

            if ssl_context == ‘adhoc‘:

                ssl_context = generate_adhoc_ssl_context()

            # If we are on Python 2 the return value from socket.fromfd

            # is an internal socket object but what we need for ssl wrap

            # is the wrapper around it :(

            sock = self.socket

            if PY2 and not isinstance(sock, socket.socket):

                sock = socket.socket(sock.family, sock.type, sock.proto, sock)

            self.socket = ssl_context.wrap_socket(sock, server_side=True)

            self.ssl_context = ssl_context

        else:

            self.ssl_context = None

 

    def log(self, type, message, *args):

        _log(type, message, *args)

 

    def serve_forever(self):

        self.shutdown_signal = False

        try:

            HTTPServer.serve_forever(self)

        except KeyboardInterrupt:

            pass

        finally:

            self.server_close()

 

    def handle_error(self, request, client_address):

        if self.passthrough_errors:

            raise

        return HTTPServer.handle_error(self, request, client_address)

 

    def get_request(self):

        con, info = self.socket.accept()

        return con, info

 

2.      请求处理

上面的部分是服务器的声明及运行,下面写一下flask服务器是如何处理请求的。

WSGI部分暂且略过,具体可看:https://www.cnblogs.com/steinliber/p/5133386.html

总而言之,它通过application_iter = app(environ, start_response)将请求体传给了flask的app。

 

2.1.1.   wsgi_app()

接下来,当http请求从server发送过来的时候,会调用__call__()方法,最后实际是调用了wsgi_app功能并传入environ和start_response。

代码如下:

   def wsgi_app(self, environ, start_response):

        """The actual WSGI application.

        :param environ: A WSGI environment.

        :param start_response: A callable accepting a status code,

            a list of headers, and an optional exception context to

            start the response.

        """

        ctx = self.request_context(environ)

        error = None

        try:

            try:

                ctx.push()

                response = self.full_dispatch_request()

            except Exception as e:

                error = e

                response = self.handle_exception(e)

            except:

                error = sys.exc_info()[1]

                raise

            return response(environ, start_response)

        finally:

            if self.should_ignore_error(error):

                error = None

            ctx.auto_pop(error)

 

    def __call__(self, environ, start_response):

        """The WSGI server calls the Flask application object as the

        WSGI application. This calls :meth:`wsgi_app` which can be

        wrapped to applying middleware."""

        return self.wsgi_app(environ, start_response)

 

 

 

response是在这里生成的。

上下文处理语句:

ctx = self.request_context(environ)

 

核心语句:

response = self.full_dispatch_request()

 

2.1.2.   full_dispatch_request

找到full_dispatch_request:

def full_dispatch_request(self):
    """Dispatches the request and on top of that performs request
    pre and postprocessing as well as HTTP exception catching and
    error handling.
    """
   
self.try_trigger_before_first_request_functions()
    try:
        request_started.send(self)
        rv = self.preprocess_request()
        if rv is None:
            rv = self.dispatch_request()
    except Exception as e:
        rv = self.handle_user_exception(e)
    return self.finalize_request(rv)

 

try_trigger_before_first_request_function()用于判断是否是第一个请求,然后是否执行           

for func in self.before_first_request_funcs:

                func()

request_started是信号机制通知请求开始处理,preprocess_request会调用app中注册的请求前函数,若函数的返回值不是None,response的内容就设为该返回值。否则就调用dispatch_request来找到对应的视图函数得到返回值

 

preprocess_request()方法的话,主要是进行flask的hook钩子, before_request功能的实现;

 

2.1.3.   dispatch_request()

下一句:

    try:
        request_started.send(self)
        rv = self.preprocess_request()
        if rv is None:
            rv = self.dispatch_request()

一个http请求到了这里,实际上已经完成了从wsgi部分的过渡,进入到了寻找响应的阶段了,一个请求通过url进来以后,app怎么知道要如何响应呢?

就是通过dispatch_request方法来进行请求判定和分发。

 

    def dispatch_request(self):

        """Does the request dispatching.  Matches the URL and returns the

        return value of the view or error handler.  This does not have to

        be a response object.  In order to convert the return value to a

        proper response object, call :func:`make_response`.

        """

        req = _request_ctx_stack.top.request

        if req.routing_exception is not None:

            self.raise_routing_exception(req)

        rule = req.url_rule

        # if we provide automatic options for this URL and the

        # request came with the OPTIONS method, reply automatically

        if getattr(rule, ‘provide_automatic_options‘, False) \

           and req.method == ‘OPTIONS‘:

            return self.make_default_options_response()

        # otherwise dispatch to the handler for that endpoint

        return self.view_functions[rule.endpoint](**req.view_args)

 

self.view_functions是通过路由模块产生的endpoint与视图函数相对应的字典。这个就能返回视图函数要返回的值。

 

2.1.4.   finalize_request()

接下来返回full_dispatch_request

return self.finalize_request(rv)

对响应进行处理,主要是标准化,通过make_response来将其转化为response的对象

def finalize_request(self, rv, from_error_handler=False):

        """Given the return value from a view function this finalizes

        the request by converting it into a response and invoking the

        postprocessing functions.  This is invoked for both normal

        request dispatching as well as error handlers.

 

        Because this means that it might be called as a result of a

        failure a special safe mode is available which can be enabled

        with the `from_error_handler` flag.  If enabled, failures in

        response processing will be logged and otherwise ignored.

 

        :internal:

        """

        response = self.make_response(rv)

        try:

            response = self.process_response(response)

            request_finished.send(self, response=response)

        except Exception:

            if not from_error_handler:

                raise

            self.logger.exception(‘Request finalizing failed with an ‘

                                  ‘error while handling an error‘)

        return response

 

 

3.      总结

对flask程序结构有了初步了解,理解了从请求到WSGI到flask的处理流程。

从请求到响应的流程图:

 

 

4. 参考文档

https://www.jianshu.com/p/2a2407f66438

https://blog.csdn.net/bestallen/article/details/54342120

 

笔记-flask-原理及请求处理流程

原文:https://www.cnblogs.com/wodeboke-y/p/10587995.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!