Flask源码篇:wsgi、Werkzeug与Flask启动工作流程

源码系列:

Flask源码篇:Flask路由规则与请求匹配过程
Flask源码篇:2w字彻底吃透Flask是上下文原理

前面介绍Flask的时候提到过wsgi和Werkzeug,下面来从源码详细看下两个到底是什么,和Flask的工作流程有什么关系。

如果不想看过程分析,可以看最后的总结,还有流程图,也可以一看就懂!

1 wsgi介绍

wsgi全称为Web Server Gateway Interface,是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。类似于Java语言中的Servlet。

实际上wsgi是一种协议或规范,它规范了web服务器如何与Flask等python web服务器进行交互和通信。

通常,一个生产环境完整的web请求有如下流程:

在这里插入图片描述

实际上,在我们使用wsgi时,并不是一个单独的网关服务器,通常是继承在python后端服务器中。很多后端框架,比如Flask、Django等都自己实现了。其中Flask就是自己实现了Werkzeug(根据wsgi进一步抽象了)。

WSGI的主要作用是 Web Server 和 Python Application 之间的桥梁,或者翻译官,让两者能够无障碍沟通。可以归纳为以下几点:

  1. Web服务器的责任在于监听和接收请求。在处理请求的时候调用WSGI提供的标准化接口,将请求的信息转给WSGI
  2. WSGI的责任在于“中转”请求和响应信息。WSGI接收到Web服务器提供的请求信息后可以做一些处理,之后通过标准化接口调用Web应用,并将请求信息传递给Web应用。同时,WSGI还将会处理Web应用返回的响应信息,并通过服务器返回给客户端;
  3. Web应用的责任在于接收请求信息,并且生成响应。

2 使用wsgi实现一个web应用

WSGI 通常有以下协议:

每个 python web 应用都是一个可调用(callable)的对象,函数或者一个带有__call__方法的类。__call__方法有2个参数:第一个参数是WSGI的environ,第二个参数是一个start_response函数。environ 包含了请求的所有信息,都是一些键值对,要么是提供给server,要么提供给middleware。start_response 是 application 处理完之后需要调用的函数,参数是状态码、响应头部还有错误信息。

application 还有个非常重要的特点是:**它是可以嵌套的。**换句话说,我可以写个 application,它做的事情就是调用另外一个 application,然后再返回(类似一个 proxy)。一般来说,嵌套的最后一层是业务应用,中间就是 middleware。这样的好处是,可以解耦业务逻辑和其他功能,比如限流、认证、序列化等都实现成不同的中间层,不同的中间层和业务逻辑是不相关的,可以独立维护;而且用户也可以动态地组合不同的中间层来满足不同的需求。

基于以上特点,我们实现一个简单的python后端服务(功能上相当于Falsk),代码如下:

# 定义我们自己的python 应用
def application(environ, start_response):
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return [b"hello word"]

application 是一个函数,肯定是可调用对象,然后接收两个参数,两个参数分别是:environ和start_response

  • environ是一个字典,里面储存了HTTP request相关的所有内容,比如header、请求参数等等
  • start_response是一个WSGI 服务器传递过来的函数,用于将response header,状态码传递给Server。

调用 start_response 函数负责将响应头、状态码传递给服务器, 响应体则由application函数返回给服务器, 一个完整的http response 就由这两个函数提供。

下面使用python自带的wsgi包实现一个wsgi网关服务器:

from wsgiref.simple_server import make_server
server = make_server('localhost', 8080, application)
server.serve_forever()

一个完整的python web后端服务器代码如下:

# wsgi使用

from wsgiref.simple_server import make_server


# 定义我们自己的python 应用
def application(environ, start_response):
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return [b"hello word"]


if __name__ == "__main__":
    server = make_server('localhost', 8080, application)
    server.serve_forever()

启动后,访问127.0.0.1:8080,即可得到响应hello word。

以上就是使用自己定义的python 可调用对象application和自带的wsgi包实现了一个非常简单web应用。

3 Werkzeug介绍

Werkzeug就是一个WSGI工具包,他可以作为一个Web框架的底层库。前面我们说过,Flask主要用到了Werkzeug和jinja2两个库。

其中Werkzeug提供了Flask很核心的功能:

  1. 请求和响应对象:提供了RequestResponseRequest可以包装WSGI服务器传入的environ参数,并对其进行进一步的解析,以使我们更容易的使用请求中的参数。Response可以根据传入的参数,来发起一个特定的响应。
  2. 路由解析:Werkzeug提供了强大的路由解析功能。比如Flask框架中经常用到的RuleMap类等。
  3. 本地上下文:在许多Web程序中,本地上下文是个非常重要的概念。而实现本地上下文需要用到不同线程间数据的隔离。werkzeug.local中定义了LocalLocalStackLocalProxy等类用于实现全局数据的隔离。

Werkzeug还提供了很多工具,例如WSGI中间件、HTTP异常类、数据结构等。

后面还会详细介绍一些主要功能。

4 Flask工作流程分析

注:以下分析会着重通过源码分析Flask启动过程,Flask版本:2.0.2。

以一个最简单Flask应用来举例,代码如下:

from flask import Flask

app = Flask(__name__)


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


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

下面来分析下,Flask应用启动时到底发生了什么。

(1)创建Flask app

首先app = Flask(__name__)代码创建了一个Flask对象。它实现了wsgi应用,并且是扮演了最核心的角色。

第一个参数__name__是模块或包的名字,一般使用这个就好,也可以自己定义名字。

(2)启动Falsk app

接着看最后一行app.run()run() 方法的定义,调用了werkzeug库中的一个 run_simple() 方法,最后启动了 BaseWSGIServer 服务器。

我们看下源码:

def run(
    self,
    host: t.Optional[str] = None,
    port: t.Optional[int] = None,
    debug: t.Optional[bool] = None,
    load_dotenv: bool = True,
    **options: t.Any,
) -> None:
    # 如果从命令行启动
    if os.environ.get("FLASK_RUN_FROM_CLI") == "true":
        from .debughelpers import explain_ignored_app_run

        explain_ignored_app_run()
        return
		
    # 加载配置
    if get_load_dotenv(load_dotenv):
        cli.load_dotenv()

        # if set, let env vars override previous values
        if "FLASK_ENV" in os.environ:
            self.env = get_env()
            self.debug = get_debug_flag()
        elif "FLASK_DEBUG" in os.environ:
            self.debug = get_debug_flag()

    # debug passed to method overrides all other sources
    if debug is not None:
        self.debug = bool(debug)

    server_name = self.config.get("SERVER_NAME")
    sn_host = sn_port = None
		
    if server_name:
        sn_host, _, sn_port = server_name.partition(":")
		
    # 下面结果if是设置host、port参数默认值
    if not host:
        if sn_host:
            host = sn_host
        else:
            host = "127.0.0.1"

    if port or port == 0:
        port = int(port)
    elif sn_port:
        port = int(sn_port)
    else:
        port = 5000

    options.setdefault("use_reloader", self.debug)
    options.setdefault("use_debugger", self.debug)
    options.setdefault("threaded", True)

    cli.show_server_banner(self.env, self.debug, self.name, False)

    from werkzeug.serving import run_simple

    try:
        # 这一行是核心,调用app.run方法,实际上是执行了werkzeug服务的run_simple方法
        run_simple(t.cast(str, host), port, self, **options)
    finally:
        self._got_first_request = False

可以看到,run方法有host、post、debug、load_dotenv等参数。load_dotenv主要用来指定配置文件,加载配置的。

启动falsk app的核心就是调用werkzeug库中的一个run_simple()方法,同时传递了参数。

(3)分析run_simple方法

run_simple方法源码如下:

def run_simple(
    hostname: str,
    port: int,
    application: "WSGIApplication",
    use_reloader: bool = False,
    use_debugger: bool = False,
    use_evalex: bool = True,
    extra_files: t.Optional[t.Iterable[str]] = None,
    exclude_patterns: t.Optional[t.Iterable[str]] = None,
    reloader_interval: int = 1,
    reloader_type: str = "auto",
    threaded: bool = False,
    processes: int = 1,
    request_handler: t.Optional[t.Type[WSGIRequestHandler]] = None,
    static_files: t.Optional[t.Dict[str, t.Union[str, t.Tuple[str, str]]]] = None,
    passthrough_errors: bool = False,
    ssl_context: t.Optional[_TSSLContextArg] = None,
) -> None:
    """Start a WSGI application. Optional features include a reloader,
    multithreading and fork support.

    This function has a command-line interface too::
        python -m werkzeug.serving --help
		以下参数介绍省略
    """
    if not isinstance(port, int):
        raise TypeError("port must be an integer")
    if use_debugger:
        from .debug import DebuggedApplication

        application = DebuggedApplication(application, use_evalex)
    if static_files:
        from .middleware.shared_data import SharedDataMiddleware

        application = SharedDataMiddleware(application, static_files)

    def log_startup(sock: socket.socket) -> None:
        all_addresses_message = (
            " * Running on all addresses.\n"
            "   WARNING: This is a development server. Do not use it in"
            " a production deployment."
        )

        if sock.family == af_unix:
            _log("info", " * Running on %s (Press CTRL+C to quit)", hostname)
        else:
            if hostname == "0.0.0.0":
                _log("warning", all_addresses_message)
                display_hostname = get_interface_ip(socket.AF_INET)
            elif hostname == "::":
                _log("warning", all_addresses_message)
                display_hostname = get_interface_ip(socket.AF_INET6)
            else:
                display_hostname = hostname

            if ":" in display_hostname:
                display_hostname = f"[{display_hostname}]"

            _log(
                "info",
                " * Running on %s://%s:%d/ (Press CTRL+C to quit)",
                "http" if ssl_context is None else "https",
                display_hostname,
                sock.getsockname()[1],
            )

    def inner() -> None:
        try:
            fd: t.Optional[int] = 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)
        # 这里是inner函数的核心,调用了wsgi的make_server创建了一个wsgi服务器,并且执行serve_forever方法
        srv.serve_forever()

    if use_reloader:
        if not is_running_from_reloader():
            if port == 0 and not can_open_by_fd:
                raise ValueError(
                    "Cannot bind to a random port with enabled "
                    "reloader if the Python interpreter does "
                    "not support socket opening by fd."
                )
            address_family = select_address_family(hostname, port)
            server_address = get_sockaddr(hostname, port, address_family)
            s = socket.socket(address_family, socket.SOCK_STREAM)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.bind(server_address)
            s.set_inheritable(True)

            if can_open_by_fd:
                os.environ["WERKZEUG_SERVER_FD"] = str(s.fileno())
                s.listen(LISTEN_QUEUE)
                log_startup(s)
            else:
                s.close()
                if address_family == af_unix:
                    server_address = t.cast(str, server_address)
                    _log("info", "Unlinking %s", server_address)
                    os.unlink(server_address)

        from ._reloader import run_with_reloader as _rwr
				# 这里是主要代码,把inner函数传递给run_with_reloader函数,开启一个线程执行inner
        _rwr(
            inner,
            extra_files=extra_files,
            exclude_patterns=exclude_patterns,
            interval=reloader_interval,
            reloader_type=reloader_type,
        )
    else:
        inner()

我们只看最核心的代码部分即可。

可以看到方法里定义了一个非常重要的函数inner()use_reloader这个参数也是从run方法传递过来的,默认为False。

如果use_reloader为True,最后流程会走到执行run_with_reloader方法,及run_with_reloader(inner, extra_files, reloader_interval, reloader_type)

run_with_reloader代码如下:

def run_with_reloader(
    main_func: t.Callable[[], None],
    extra_files: t.Optional[t.Iterable[str]] = None,
    exclude_patterns: t.Optional[t.Iterable[str]] = None,
    interval: t.Union[int, float] = 1,
    reloader_type: str = "auto",
) -> None:
    """Run the given function in an independent Python interpreter."""
    import signal

    signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
    reloader = reloader_loops[reloader_type](
        extra_files=extra_files, exclude_patterns=exclude_patterns, interval=interval
    )

    try:
        if os.environ.get("WERKZEUG_RUN_MAIN") == "true":
            ensure_echo_on()
            # 这里是主要代码:把我们上面传递过来的inner函数作为主函数,开始一个新线程(守护线程)
            t = threading.Thread(target=main_func, args=())
            t.daemon = True

            with reloader:
                t.start()
                reloader.run()
        else:
            sys.exit(reloader.restart_with_reloader())
    except KeyboardInterrupt:
        pass

其主要功能就是把我们上面传递过来的inner函数作为主函数,开始一个新线程(守护线程),本质还是执行inner函数。

如果use_reloader为False,则直接执行inner函数。

所以,inner()函数是run_simple()函数的核心。

下面看看inner函数主要做了什么。把inner函数代码单独拿出来,如下:

def inner() -> None:
        try:
            fd: t.Optional[int] = 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)
        # 这里是inner函数的核心,调用了wsgi的make_server创建了一个wsgi服务器,并且执行serve_forever方法
        srv.serve_forever()

inner()函数的核心就是:调用了make_server()方法,并且执行了serve_forever方法。

下面来分析下make_server方法做了什么。

(4)分析make_server方法

make_server()方法是werkzeug包内的方法。其源码如下:

def make_server(
    host: str,
    port: int,
    app: "WSGIApplication",
    threaded: bool = False,
    processes: int = 1,
    request_handler: t.Optional[t.Type[WSGIRequestHandler]] = None,
    passthrough_errors: bool = False,
    ssl_context: t.Optional[_TSSLContextArg] = None,
    fd: t.Optional[int] = None,
) -> BaseWSGIServer:
    """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:
      	# 返回了一个BaseWSGIServer对象
        return BaseWSGIServer(
            host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd
        )

首先这个方法接受了run_simple函数传递进来的参数:host、port、app(Flask对象)、thread(线程)、 processes = 1 (单进程 )。

最重要的是这个方法返回了一个BaseWSGIServer对象(ThreadedWSGIServer和ForkingWSGIServer都继承了它),后面会启动BaseWSGIServer,创造一个单线程,单进程的WSGI server

(5)分析BaseWSGIServer类

BaseWSGIServer源码如下:

class BaseWSGIServer(HTTPServer):

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

    multithread = False
    multiprocess = False
    request_queue_size = LISTEN_QUEUE

    def __init__(
        self,
        host: str,
        port: int,
        app: "WSGIApplication",
        handler: t.Optional[t.Type[WSGIRequestHandler]] = None,
        passthrough_errors: bool = False,
        ssl_context: t.Optional[_TSSLContextArg] = None,
        fd: t.Optional[int] = None,
    ) -> None:
        if handler is None:
          	# wsgi的请求处理器
            handler = WSGIRequestHandler

        self.address_family = select_address_family(host, port)

        if fd is not None:
            real_sock = socket.fromfd(fd, self.address_family, socket.SOCK_STREAM)
            port = 0

         # 获取服务地址
        server_address = get_sockaddr(host, int(port), self.address_family)

        # remove socket file if it already exists
        if self.address_family == af_unix:
            server_address = t.cast(str, server_address)

            if os.path.exists(server_address):
                os.unlink(server_address)

        # 调用父类HTTPServer的初始化方法,参数有地址和请求处理器
        super().__init__(server_address, handler)  # type: ignore

        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()

            self.socket = ssl_context.wrap_socket(self.socket, server_side=True)
            self.ssl_context: t.Optional["ssl.SSLContext"] = ssl_context
        else:
            self.ssl_context = None

    def log(self, type: str, message: str, *args: t.Any) -> None:
        _log(type, message, *args)

    def serve_forever(self, poll_interval: float = 0.5) -> None:
        """核心方法:调用了父类HTTPServer的serve_forever方法"""
        self.shutdown_signal = False
        try:
            super().serve_forever(poll_interval=poll_interval)
        except KeyboardInterrupt:
            pass
        finally:
            self.server_close()

    def handle_error(self, request: t.Any, client_address: t.Tuple[str, int]) -> None:
        if self.passthrough_errors:
            raise

        return super().handle_error(request, client_address)

BaseWSGIServer的核心就是继承了HTTPServer,其中初始化方法里主要做了两件事,一个是使用get_sockaddr方法找到服务地址,还有一个就是使用父类HTTPServer的初始化方法,参数有地址和请求处理器WSGIRequestHandler

其中最核心的方法就是前面inner方法里提到过的serve_forever()方法。
这个方法其实也是调用了父类BaseServer的serve_forever方法。BaseServer是socketserver模块下的一个类。

继承关系是:BaseWSGIServer继承HTTPServer,HTTPServer继承socketserver.TCPServer,socketserver.TCPServer继承BaseServer。

(6)分析HTTPServer类

HTTPServer在http.server.py 模块中。其源码如下:

class HTTPServer(socketserver.TCPServer):

    allow_reuse_address = 1    # Seems to make sense in testing environment

    def server_bind(self):
        """Override server_bind to store the server name."""
        socketserver.TCPServer.server_bind(self)
        host, port = self.server_address[:2]
        self.server_name = socket.getfqdn(host)
        self.server_port = port

可以看到

  1. HTTPServer继承了socketserver.TCPServer类,socketserver.TCPServer类属于socketserver模块下下一个TCP服务类,不再做过多的解析了;
  2. 重写了一个server_bind方法,用于绑定服务器地址和端口。

所以HTTPServer主要作用是于绑定服务器地址和端口。

下面着重看下serve_forever方法。

(7)分析serve_forever方法

上面说了,这个方法其实也是调用了父类BaseServerserve_forever方法。

其代码如下:

def serve_forever(self, poll_interval=0.5):
    """Handle one request at a time until shutdown.

    Polls for shutdown every poll_interval seconds. Ignores
    self.timeout. If you need to do periodic tasks, do them in
    another thread.
    """
    self.__is_shut_down.clear()
    try:
        # XXX: Consider using another file descriptor or connecting to the
        # socket to wake this up instead of polling. Polling reduces our
        # responsiveness to a shutdown request and wastes cpu at all other
        # times.
        with _ServerSelector() as selector:
            selector.register(self, selectors.EVENT_READ)

            while not self.__shutdown_request:
                ready = selector.select(poll_interval)
                # bpo-35017: shutdown() called during select(), exit immediately.
                if self.__shutdown_request:
                    break
                if ready:
                    self._handle_request_noblock()

                self.service_actions()
    finally:
        self.__shutdown_request = False
        self.__is_shut_down.set()

可以看到主要作用就是写了一个死循环,然后不断处理接收到的请求。

(8)分析WSGIRequestHandler类

WSGIRequestHandler类的源码比较多,其中定义了很多方法用来处理请求。主要源码如下:

class WSGIRequestHandler(BaseHTTPRequestHandler):
    """A request handler that implements WSGI dispatching."""

    server: "BaseWSGIServer"
    
    def run_wsgi(self) -> None:
        if self.headers.get("Expect", "").lower().strip() == "100-continue":
            self.wfile.write(b"HTTP/1.1 100 Continue\r\n\r\n")

        self.environ = environ = self.make_environ()
        status_set: t.Optional[str] = None
        headers_set: t.Optional[t.List[t.Tuple[str, str]]] = None
        status_sent: t.Optional[str] = None
        headers_sent: t.Optional[t.List[t.Tuple[str, str]]] = None

        def write(data: bytes) -> None:
            nonlocal status_sent, headers_sent
            assert status_set is not None, "write() before start_response"
            assert headers_set is not None, "write() before start_response"
            if status_sent is None:
                status_sent = status_set
                headers_sent = headers_set
                try:
                    code_str, msg = status_sent.split(None, 1)
                except ValueError:
                    code_str, msg = status_sent, ""
                code = int(code_str)
                self.send_response(code, msg)
                header_keys = set()
                for key, value in headers_sent:
                    self.send_header(key, value)
                    key = key.lower()
                    header_keys.add(key)
                if not (
                    "content-length" in header_keys
                    or environ["REQUEST_METHOD"] == "HEAD"
                    or code < 200
                    or code in (204, 304)
                ):
                    self.close_connection = True
                    self.send_header("Connection", "close")
                if "server" not in header_keys:
                    self.send_header("Server", self.version_string())
                if "date" not in header_keys:
                    self.send_header("Date", self.date_time_string())
                self.end_headers()

            assert isinstance(data, bytes), "applications must write bytes"
            self.wfile.write(data)
            self.wfile.flush()
        
        def start_response(status, headers, exc_info=None):  # type: ignore
            nonlocal status_set, headers_set
            if exc_info:
                try:
                    if headers_sent:
                        raise exc_info[1].with_traceback(exc_info[2])
                finally:
                    exc_info = None
            elif headers_set:
                raise AssertionError("Headers already set")
            status_set = status
            headers_set = headers
            return write

        def execute(app: "WSGIApplication") -> None:
          	# 这里的app注意就是我们上面run_simple方法传递进来的app级Flask app
            # 执行execute方法会调用app(),这就是为什么要求我们的Flask对象要可被调用(实现__call__方法)
            # 调用app时把environ环境参数和start_response传进去,与第二节里返示例完全一致了
            application_iter = app(environ, start_response)
            try:
                for data in application_iter:
                    write(data)
                if not headers_sent:
                    write(b"")
            finally:
                if hasattr(application_iter, "close"):
                    application_iter.close()  # type: ignore

        try:
           # run_wsgi的核心是执行execute方法
            execute(self.server.app)
        except (ConnectionError, socket.timeout) as e:
            self.connection_dropped(e, environ)
        except Exception:
            if self.server.passthrough_errors:
                raise
            from .debug.tbtools import get_current_traceback

            traceback = get_current_traceback(ignore_system_exceptions=True)
            try:
                # if we haven't yet sent the headers but they are set
                # we roll back to be able to set them again.
                if status_sent is None:
                    status_set = None
                    headers_set = None
                execute(InternalServerError())
            except Exception:
                pass
            self.server.log("error", "Error on request:\n%s", traceback.plaintext)
            
    def handle_one_request(self) -> None:
        """Handle a single HTTP request."""
        self.raw_requestline = self.rfile.readline()
        if not self.raw_requestline:
          self.close_connection = True
        elif self.parse_request():
          self.run_wsgi()

非常重要:

首先handle_one_request(self)方法,主要用来处理一个请求。当一个请求进来时就会调用这个方法。接着handle_one_request调用了核心方法run_wsgi()方法。run_wsgi()方法的核心就是execute方法。其中最最重要的是execute方法里application_iter = app(environ, start_response)这行代码。

这里的app注意就是我们上面run_simple方法传递进来的app及Flask app。执行execute方法会调用app(),这就是为什么要求我们的Flask对象要可被调用(实现__call__方法)。调用app__call__方法时把environ环境参数和start_response传进去,与第二节里返示例完全一致了

5 Flask工作流程总结

经过以上的分析,可以看到,Flask的工作流程大致如下:

  1. 创建一个Flask对象app,并执行app.run()方法;
  2. app.run()方法主要调用了run_simple()方法;
  3. run_simple()方法主要执行了内部的inner()方法;
  4. inner()方法主要执行了make_server()方法,返回了一个BaseWSGIServer对象srv,srv对象有个WSGIRequestHandler对象属性,用于处理请求;
  5. 然后inner()方法执行了BaseWSGIServer对象的serve_forever()方法,这个方法主要是写了一个死循环,用于处理不断接收到的请求;
  6. 当一个请求进来时,会先执行WSGIRequestHandler对象的handle_one_request()方法;
  7. handle_one_request()方法调用了核心方法run_wsgi()方法;
  8. run_wsgi()方法的核心就是execute()方法;
  9. execute方法里核心代码是application_iter = app(environ, start_response),app就是上面run_simple方法传递进来的app,即Flask app,这样就会调用app的__call__方法,调用app__call__方法时把environ环境参数和start_response传进去。
  10. 接着就是执行app的__call__方法来处理请求了。

整个流程图可以概括如下图:

在这里插入图片描述

可以看到整个Falsk启动过程中,最重要的是创建了BaseWSGIServer对象,并执行了对象的serve_forever()方法。这样一个wsgi服务器就创建并启动好了,当请求进来时,就可以处理请求,并把请求header和参数等传递给Flask app的__call__方法,接着就是根据请求做出响应了。

所以wsgi在其中的作用就是一个"翻译官"或"媒介",接受到客户端的请求,进行处理和包装,再传给Falsk应用服务器。

参考:
https://blog.csdn.net/sinat_36651044/article/details/77462831

https://www.cnblogs.com/skyflask/p/9193828.html

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

https://cizixs.com/2017/01/11/flask-insight-start-process/

https://blog.csdn.net/lantian_123/article/details/109396576?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-109396576-blog-122802770.pc_relevant_3mothn_strategy_recovery&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-109396576-blog-122802770.pc_relevant_3mothn_strategy_recovery&utm_relevant_index=1

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ethan-running

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值