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

news2025/2/27 9:38:09

目录

  • 1 wsgi介绍
  • 2 使用wsgi实现一个web应用
  • 3 Werkzeug介绍
  • 4 Flask工作流程分析
    • (1)创建Flask app
    • (2)启动Falsk app
    • (3)分析run_simple方法
    • (4)分析make_server方法
    • (5)分析BaseWSGIServer类
    • (6)分析HTTPServer类
    • (7)分析serve_forever方法
    • (8)分析WSGIRequestHandler类
  • 5 Flask工作流程总结

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

1 wsgi介绍

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

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

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

在这里插入图片描述

实际上,在我们使用wsgi时,并不是一个单独的网关服务器,通常是继承在pytho后端服务器中。很多后端框架,比如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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/379811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内容分发网络

介绍 CDN 内容分发网络&#xff08;英语&#xff1a;Content Delivery Network 或 Content Distribution Network&#xff0c;缩写&#xff1a;CDN&#xff09;是建立并覆盖在承载网上&#xff0c;由不同区域的服务器组成的分布式网络。将源站资源缓存到全国各地的边缘服务器&…

【数据库视图】简单学习视图,了解一些视图的简单功能

前言&#xff1a; 大家好&#xff0c;我是良辰丫&#x1f345;&#x1f345;&#x1f345;&#xff0c;今天我想带大家去了解一下数据库的视图,虽然视图这个东西在很多地方(各种公司以及项目)已经不再用了,但是许多大学生在考试的时候涉及,&#x1f6f4;&#x1f6f4;&#x1f…

【c++】STL常用容器5—list容器

文章目录list基本概念list构造函数list赋值和交换list大小操作list插入和删除list数据存取list反转和排序list基本概念 功能&#xff1a;将数据进行链式存储。 链表&#xff08;list&#xff09;是一种物理存储单元上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链…

京东物流实时风控实践

摘要&#xff1a;本文整理自京东风控数据产品组架构师周文跃&#xff0c;在 FFA 2022 实时风控专场的分享。本篇内容主要分为六个部分&#xff1a;1. 京东物流业务介绍2. 物流风控场景概括3. 物流风控平台建设4. Flink 赋能5. 技术挑战6. 未来规划Tips&#xff1a;点击「阅读原…

操作系统权限提升(二十)之Linux提权-计划任务提权

系列文章 操作系统权限提升(十八)之Linux提权-内核提权 操作系统权限提升(十九)之Linux提权-SUID提权 计划任务提权 计划任务提权原理 linux计划任务提权是因为权限配置不当&#xff0c;计划任务以root权限运行&#xff0c;低权限的用户可以修改计划任务的文件&#xff0c;…

docker启动容器服务之后访问失败

关于docker启动容器服务之后&#xff0c;宿主机访问失败&#xff08;解决方法&#xff09; 注&#xff1a;在进行docker容器启动宿主机进行容器访问时&#xff0c;无需进行网络的配置&#xff0c;docker容器在启动时会自动解决 第一种原因及修改方法 在进行启动的时候&#…

JVM虚拟机概述(1)

1.JVM概述 1.1为什么要学习JVM 通过学习JVM ( java Virtual Machine )可以帮助我们理解java程序运行的过程&#xff0c;了解虚拟机中各种机制的实现原理。为后期写出优质的代码做好准备&#xff0c;为向更高的层次提升打好基础。 1.2虚拟机 虚拟机的本质就是在windows中&…

深入浅出的学习傅里叶变换

学习傅里叶变换需要面对大量的数学公式&#xff0c;数学功底较差的同学听到傅里叶变换就头疼。事实上&#xff0c;许多数学功底好的数字信号处理专业的同学也不一定理解傅里叶变换的真实含义&#xff0c;不能做到学以致用&#xff01; 事实上&#xff0c;傅里叶变换的相关运算…

敏捷-期末

什么是敏捷开发&#xff1f; 敏捷开发(Agile Development)是一种以人为核心、迭代、循序渐进的开发方法。 怎么理解呢&#xff1f;它不是一门技术&#xff0c;它是一种开发方法&#xff0c;也就是一种软件开发的流程&#xff0c;它会指导我们用规定的环节去一步一步完成项目的开…

阿里云服务器ECS的功能特性有哪些?

本文介绍云服务器ECS的功能特性&#xff0c;帮助您更好地了解和使用云服务器ECS。 1、实例 实例是云上的虚拟计算服务器&#xff0c;内含vCPU、内存、操作系统、网络、磁盘等基础组件。您可以使用阿里云提供的控制台、API等管理工具创建和管理ECS实例&#xff0c;像使用本地服…

常用的 JVM 参数(第三章)

《实战Java虚拟机&#xff1a;JVM故障诊断与性能优化 (第2版)》 第三章 常用的 JVM 参数 3.1. 掌握跟踪调试参数 - 一切运行都有迹可循 参数类型参数作用备注GC 参数-JVM 提供了一些跟踪系统状态的参数&#xff0c;使用给定的参数执行 JVM&#xff0c;就可以在系统运行时打印…

基于ssm框架实现家庭理财收支系统(源码+数据库+文档)

一、项目简介 本项目是一套基于ssm框架实现家庭理财收支系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c…

【谷粒学院】微信扫码登录(199~206)

199.OAuth2介绍 OAuth2是什么&#xff1f; OAuth2是针对特定问题的一种解决方案 主要可以解决两个问题&#xff1a;开放系统间授权、分布式访问问题 一、OAuth2解决什么问题 1、OAuth2提出的背景 照片拥有者想要在云冲印服务上打印照片&#xff0c;云冲印服务需要访问云存储服…

_Linux (HTTP协议)

文章目录1. 认识URL2. urlencode和urldecode3. HTTP协议格式3-1. HTTP请求3-1. HTTP响应4. HTTP的方法5. HTTP的状态码6. TTP常见Header7. 最简单的HTTP服务器虽然我们说, 应用层协议是我们程序猿自己定的但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我…

Java基础:JDK7-时间Date

JDK7以前时间相关类 1.Date Date date new Date(); , sout(date)得到的是现在所处位置的时间 Date date new Date(0L); , sout(date)得到的是时间原点也就是1970年1月1日08:00(东八区). date.setTime(1000L); sout(date)得到的是时间原点后一秒钟的时间 long time date.g…

【matplotlib】可视化解决方案——如何设置matplotlib风格集

概述 一般情况下&#xff0c;为了获得数据可视化展示效果&#xff0c;在代码实现的尾部会调用 plt.show()&#xff0c;这种情况下会调用默认的绘图风格&#xff0c;即白色底色&#xff0c;字体和线条呈现黑色。在某些情况下会显得很不协调。如果想要将展示效果以一个固定的风格…

华为OD机试用Python实现 -【称砝码】(2023-2-26华为OD机试人员抽到的真题)

华为OD机试题 华为 OD 机试 300 题大纲称砝码题目输入输出示例一输入输出说明:Python 代码展示编码思路华为 OD 机试 300 题大纲 参加华为 od 机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:blog.csdn.net/hihell/categor…

【JavaSE】集合(Map)

文章目录1.Map接口1.1常用方法1.2 遍历2.HashMap2.1Hash表数据结构2.2 Map.put实现原理2.3 Map.get实现原理2.4扩容2.5 同时重写hashCode和equals2.6 JAVA8对HashMap改进3.Hashtable4.Properties5.TreeMap5.1 概述5.2 自定义类实现Comparable6.集合工具类Collections1.Map接口 …

Linux | 1. 挂载新硬盘与磁盘管理

如有错误&#xff0c;恳请指出。 1. Ubuntu挂载新硬盘 查看磁盘状态&#xff1a;sudo fdisk -l 1&#xff09;为新硬盘分区 使用 fdisk 指令对 /dev/sdb 进行分区操作&#xff1a;sudo fdisk /dev/sdb。进入分区工具后&#xff0c;我们可以输入 m 看指令说明&#xff0c;注意…

(四十八)大白话线上数据库不确定性的性能抖动优化实践(上)

之前我们花费了很大篇幅来给大家深入和细致的讲解数据库在执行增删改这类更新语句时候的底层原理&#xff0c;这里涉及到了很多数据库内核级的概念&#xff0c;比如buffer pool、redo log buffer、lru/flush链表&#xff0c;等等&#xff0c;大家对数据库执行更新语句的原理都有…