编程学习网 > 编程语言 > Python > flask框架运行原理(flask框架基本原理)
2022
12-05

flask框架运行原理(flask框架基本原理)

今天编程学习网为大家讲解flask框架运行原理,有需要的小伙伴可以参考一下:

通过阅读Flask的源码来学习下运行原理

1、启动,从请求到响应的过程

一个最简单的程序HelloWrold.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World!'

if __name__ == '__main__':
    app.run()

可以看到,主要运行服务的代码只有2行

实例化 app = Flask(__name__)

运行 app.run()

实例化FLASK后,运行其中的run函数

run函数中的代码,传入一些配置参数后,实际运行的是werkzeug.serving.run_simple(host, port, self, **options)

Flask的底层运行的服务实际是调用werkzeug.serving.run_simple()后做了一些封装

run_simple()传入的self就是app,而且会以app()的形式运行

app()相当于执行app.__call__()

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:
            self._got_first_request = False

app.__call__(),执行了一行self.wsgi_app(environ, start_response)

按照wsgi协议,

environ:一个包含所有HTTP请求信息的dict对象

start_response:一个发送HTTP响应的函数

 def __call__(self, environ, start_response):
  return self.wsgi_app(environ, start_response)

environ被经过一系列封装处理后,最终返回了封装了request和session的Request类的对象,赋值给ctx

这个ctx即为请求上下文,下文中再说 

最后返回response(environ, start_response)

def wsgi_app(self, environ, start_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)

然后进入full_dispatch_request(self)

执行 self.try_trigger_before_first_request_functions()即装饰器@before_first_request装饰所有函数

执行 rv = self.preprocess_request()方法 即@before_request装饰所有函数

return self.finalize_request(rv)方法 即@after_request装饰所有函数

进入self.finalize_request(rv),response = self.process_response(response)

def full_dispatch_request(self):
        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)
从请求上下文栈中取出request,进行路由匹配,执行视图函数


def dispatch_request(self):
        req = _request_ctx_stack.top.request
        if req.routing_exception is not None:
            self.raise_routing_exception(req)
        rule = req.url_rule
        if getattr(rule, 'provide_automatic_options', False) \
           and req.method == 'OPTIONS':
            return self.make_default_options_response()
        return self.view_functions[rule.endpoint](**req.view_args)

2、路由

通过HelloWrold.py可以看到路由是通过一个装饰器@app.route('/')添加进来的

找个装饰器实际运行的代码self.add_url_rule(rule, endpoint, f, **options)

 def route(self, rule, **options):
       def decorator(f):
           endpoint = options.pop('endpoint', None)
             self.add_url_rule(rule, endpoint, f, **options)
             return f
        return decorator

endpoint是路由的唯一标识,如果为空,则把函数名赋值给endpoint

实际添加路由的self.url_map.add(rule),该函数来自于self.url_map= werkzeug.routing.Map(),在这里进行路由的添加

路由分发上文中有

@setupmethod
    def add_url_rule(self, rule, endpoint=None, view_func=None,
        if endpoint is None:
            endpoint = _endpoint_from_view_func(view_func)
        options['endpoint'] = endpoint
        methods = options.pop('methods', None)

        rule = self.url_rule_class(rule, methods=methods, **options)
        rule.provide_automatic_options = provide_automatic_options

        self.url_map.add(rule)
        if view_func is not None:
            old_func = self.view_functions.get(endpoint)
            if old_func is not None and old_func != view_func:
                raise AssertionError('View function mapping is overwriting an '
                                     'existing endpoint function: %s' % endpoint)
            self.view_functions[endpoint] = view_func

3、本地上下文

FLask上下文包含两种,请求上下文(ctx)、程序上下文(ctx_app),原理相同。

包括全局变量request、session、current_app、g

都是通过本地代理LocalProxy来实例化出来的

1 _request_ctx_stack = LocalStack()
2 _app_ctx_stack = LocalStack()
3 current_app = LocalProxy(_find_app)
4 request = LocalProxy(partial(_lookup_req_object, 'request'))
5 session = LocalProxy(partial(_lookup_req_object, 'session'))
6 g = LocalProxy(partial(_lookup_app_object, 'g'))

3.1 全局变量

reques 全局请求对象t

session 全局session对象

current_app 当前app实例

g 一个可存值的对象

全局变量通过本地代理LocalProxy(local)生成

传入具体对象,例如request,在通过代理从request中取值

上下文的push和pop是动态进行的

使用代理来取值,可以拥有动态的获取上下文对象的能力

class LocalProxy(object):

    __slots__ = ('__local', '__dict__', '__name__', '__wrapped__')

    def __init__(self, local, name=None):
        object.__setattr__(self, '_LocalProxy__local', local)
        object.__setattr__(self, '__name__', name)
        if callable(local) and not hasattr(local, '__release_local__'):
            # "local" is a callable that is not an instance of Local or
            # LocalManager: mark it as a wrapped function.
            object.__setattr__(self, '__wrapped__', local)

    def _get_current_object(self):
        if not hasattr(self.__local, '__release_local__'):
            return self.__local()
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError('no object bound to %s' % self.__name__)

    @property
    def __dict__(self):
        try:
            return self._get_current_object().__dict__
        except RuntimeError:
            raise AttributeError('__dict__')

request、session的偏函数partial(_lookup_req_object, name)

传入对应的name,从请求上下文中获取具体的对象

1 def _lookup_req_object(name):
2     top = _request_ctx_stack.top
3     if top is None:
4         raise RuntimeError(_request_ctx_err_msg)
5     return getattr(top, name)

g的偏函数partial_lookup_app_object(name)

从程序上下文中获取具体的对象

1 def _lookup_app_object(name):
2     top = _app_ctx_stack.top
3     if top is None:
4         raise RuntimeError(_app_ctx_err_msg)
5     return getattr(top, name)
current_app从程勋上下文获取当前的app


1 def _find_app():
2     top = _app_ctx_stack.top
3     if top is None:
4         raise RuntimeError(_app_ctx_err_msg)
5     return top.app

3.2 请求上下文

根据上文中的请求响应过程,请求进来后先wsgi_app()

创建了ctx上下文,从上文中得知ctx是封装了request和session的Request类的对象,然后执行push()

1 def wsgi_app(self, environ, start_response):
2     ctx = self.request_context(environ)
3     ctx.push()

这是ctx.push()的代码

实际运行了_request_ctx_stack.push()

进行了各种栈操作,再看看_request_ctx_stack栈是如何工作的

def push(self):
        top = _request_ctx_stack.top
        if top is not None and top.preserved:
            top.pop(top._preserved_exc)
        app_ctx = _app_ctx_stack.top
        if app_ctx is None or app_ctx.app != self.app:
            app_ctx = self.app.app_context()
            app_ctx.push()
            self._implicit_app_ctx_stack.append(app_ctx)
        else:
            self._implicit_app_ctx_stack.append(None)

        if hasattr(sys, 'exc_clear'):
            sys.exc_clear()

        _request_ctx_stack.push(self)

        if self.session is None:
            session_interface = self.app.session_interface
            self.session = session_interface.open_session(
                self.app, self.request
            )

            if self.session is None:
                self.session = session_interface.make_null_session(self.app)

运行_request_ctx_stack.push(self,obj)

self._local.stack = rv = [],再rv.append(obj),此时obj即为ctx

_request_ctx_stack栈中的各种操作实际都依赖于Local类,再向上看看Local类

class LocalStack(object):

    def __init__(self):
        self._local = Local()

    def __release_local__(self):
        self._local.__release_local__()

    def _get__ident_func__(self):
        return self._local.__ident_func__

    def _set__ident_func__(self, value):
        object.__setattr__(self._local, '__ident_func__', value)
    __ident_func__ = property(_get__ident_func__, _set__ident_func__)
    del _get__ident_func__, _set__ident_func__

    def __call__(self):
        def _lookup():
            rv = self.top
            if rv is None:
                raise RuntimeError('object unbound')
            return rv
        return LocalProxy(_lookup)

    def push(self, obj):
        """Pushes a new item to the stack"""
        rv = getattr(self._local, 'stack', None)
        if rv is None:
            self._local.stack = rv = []
        rv.append(obj)
        return rv

    def pop(self):
        """Removes the topmost item from the stack, will return the
        old value or `None` if the stack was already empty.
        """
        stack = getattr(self._local, 'stack', None)
        if stack is None:
            return None
        elif len(stack) == 1:
            release_local(self._local)
            return stack[-1]
        else:
            return stack.pop()

    @property
    def top(self):
        """The topmost item on the stack.  If the stack is empty,
        `None` is returned.
        """
        try:
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None

Local类构造函数中定义了两个熟悉,__storage__和__ident_func__

__ident_func__是一个获取当前线程ID的函数

__storage__是一个嵌套的字典

对Local实例进行添加属性时,调用__setattr__(),__storage__的值变为{ ident:{ name:value } },即{  线程ID: { 名称:实际数据 } }

对Local实例进行获取属性时,调用__getattr__(),根据线程ID和属性名进行取值self.__storage__[self.__ident_func__()][name]

class Local(object):
    __slots__ = ('__storage__', '__ident_func__')

    def __init__(self):
        object.__setattr__(self, '__storage__', {})
        object.__setattr__(self, '__ident_func__', get_ident)

    def __iter__(self):
        return iter(self.__storage__.items())

    def __call__(self, proxy):
        """Create a proxy for a name."""
        return LocalProxy(self, proxy)

    def __release_local__(self):
        self.__storage__.pop(self.__ident_func__(), None)

    def __getattr__(self, name):
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

    def __delattr__(self, name):
        try:
            del self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

最后ctx.push()进行的操作实际上是_request_ctx_stack栈添加了属性{  __storage__ : { 线程ID1 : { stack : [ctx] } } } 

如果是多线程运行的时候数据就是{  __storage__ : { 线程ID1 : { stack : [ctx] } , 线程ID2 : { stack : [ctx] } , 线程ID3 : { stack : [ctx] } } } 

每个线程有一个独立的栈,栈中保存的全局变量request和session为每个单独线程使用,这样就保证了线程安全

在请求进来时将request和session入栈,在生成响应后出栈

3.3 程序上下文

程序上下文的生命周期伴随请求上下文的产生和销毁

每个请求都会创建新的请求上下文栈,同时会创建新的程序上下文栈

AppContext,于请求上下文类似

1 class AppContext(object):
 2     def __init__(self, app):
 3         self.app = app
 4         self.url_adapter = app.create_url_adapter(None)
 5         self.g = app.app_ctx_globals_class()
 6         self._refcnt = 0
 7 
 8     def push(self):
10         self._refcnt += 1
11         if hasattr(sys, 'exc_clear'):
12             sys.exc_clear()
13         _app_ctx_stack.push(self)
14         appcontext_pushed.send(self.app)
15 
16     def pop(self, exc=_sentinel):
17         """Pops the app context."""
18         try:
19             self._refcnt -= 1
20             if self._refcnt <= 0:
21                 if exc is _sentinel:
22                     exc = sys.exc_info()[1]
23                 self.app.do_teardown_appcontext(exc)
24         finally:
25             rv = _app_ctx_stack.pop()
26         assert rv is self, 'Popped wrong app context.  (%r instead of %r)' \
27             % (rv, self)
28         appcontext_popped.send(self.app)
29 
30     def __enter__(self):
31         self.push()
32         return self
33 
34     def __exit__(self, exc_type, exc_value, tb):
35         self.pop(exc_value)
36 
37         if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None:
38             reraise(exc_type, exc_value, tb)
app_ctx在请求上下文push()时创建app_ctx = _app_ctx_stack.top







class RequestContext(object):
    def push(self):
        app_ctx = _app_ctx_stack.top
        if app_ctx is None or app_ctx.app != self.app:
            app_ctx = self.app.app_context()
            app_ctx.push()
            self._implicit_app_ctx_stack.append(app_ctx)
        else:
            self._implicit_app_ctx_stack.append(None)

        if hasattr(sys, 'exc_clear'):
            sys.exc_clear()

        _request_ctx_stack.push(self)

        if self.session is None:
            session_interface = self.app.session_interface
            self.session = session_interface.open_session(
                self.app, self.request
            )

            if self.session is None:
                self.session = session_interface.make_null_session(self.app)

3.4 上下文总结

Flask的上下文由请求上下文RequestContext类实例和程序上下文AppContext实例

请求上下文对象存储在请求上下文堆栈(_request_ctx_stack)

程序上下文对象存储在程序上下文堆栈(_app_ctx_stack)

每个请求都会创建新的请求上下文栈,同时会创建新的程序上下文栈

全局变量request,session保存在RequestContext实例中

全局变量current_app,g保存存在AppContext

4、总结

Flask实际上就是通过代码,将主要的处理请求的库werkzeug和模板库jinja2组合起来

通过上下文使用户可以在程序中方便的使用全局变量request、session等等,并解决了多线程线程安全的问题

小而精,其他的功能都可以通过各种三方库来扩展

以上就是“flask框架运行原理”的详细内容,想要了解更多Python教程欢迎持续关注编程学习网


扫码二维码 获取免费视频学习资料

Python编程学习

查 看2022高级编程视频教程免费获取