Falsk source code analysis: application startup process

2. Flash source code analysis: application startup process

WSGI

All python web frameworks follow the WSGI protocol

Here is a brief review of the core concepts of WSGI.

There is a very important concept in WSGI: every python web Application is a callable object. In flask, this object is app = flask (_name_) The created app is the green Application in the figure below. To run a web Application, you must have a web server, such as apache, nginx, or python gunicorn , we will talk about the WSGI servers provided by werkzeug, which are the Yellow Server part in the figure below.

How to communicate between server and application is the function of WSGI. It specifies the interface of app(environ, start_response). The server will call application and pass it two parameters: environ contains all the requested information, start_ Response is the function to be called after application processing. The parameters are status code, response header and error message.

The very important feature of WSGI application is: * * it can be nested** In other words, I can write an application. What it does is call another application, Then return (similar to a proxy). Generally speaking, the last layer of nesting is business applications, and middleware is in the middle. This has the advantage that business logic and other functions, such as flow limiting, authentication and serialization, can be decoupled into different middle layers. Different middle layers and business logic are irrelevant and can be maintained independently. Moreover, users can also combine them dynamically Different middle layers to meet different needs.

That's all for WSGI. Let's take a look at the hello world application of flash:

from flask import Flask
app = Flask(__name__)

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

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

Here, APP = flask (___) It is the Application part mentioned above, but we don't see the Server part, so it must be hidden in the app Somewhere inside run().

Start process

The application startup code is app Run(), the code of this method is as follows:

def run(self, host=None, port=None, debug=None, **options):
    """Runs the application on a local development server."""
    from werkzeug.serving import run_simple

    # If host and port are not specified, set the default values of host and port to 127.0 0.1 and 5000
    if host is None:
        host = '127.0.0.1'
    if port is None:
        server_name = self.config['SERVER_NAME']
        if server_name and ':' in server_name:
            port = int(server_name.rsplit(':', 1)[1])
        else:
            port = 5000

    # Call Werkzeug Run of serving module_ Simple function, pass in the received parameters
    # Note that the third parameter passes in self, that is, the web application to be executed
    try:
        run_simple(host, port, self, **options)
    finally:
        self._got_first_request = False

NOTE: for the convenience of reading, I have deleted comments and irrelevant parts. All the following codes will do similar processing and will not be repeated.

The content of this method is very simple: handle the parameters, then call the run_ of werkzeug. simple. Note: run_ The third parameter of simple is self, which is the flag () application we created. Because WSGI server is not the focus of this article, we won't explain it in depth. Now we only need to know its function: listening on the specified port, parsing it into WSGI format when receiving HTTP request, then calling app to execute the logic of processing. The corresponding execution logic is in werkzeug Serving: run of wsgirequesthandler_ There is a piece of code in WSGI:

def execute(app):
    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()
            application_iter = None

You can see the application_iter = app(environ, start_response) is where the calling code gets the result.

To call the app instance, it needs to be defined__ call__ Method, we find flash Corresponding content of app: Flask:

def __call__(self, environ, start_response):
    """Shortcut for :attr:`wsgi_app`."""
    return self.wsgi_app(environ, start_response)

def wsgi_app(self, environ, start_response):
    """The actual WSGI application.
    """
    # Create the request context and stack it. This will be explained in detail later
    ctx = self.request_context(environ)
    ctx.push()
    error = None

    try:
        try:
            # The correct request processing path will find the corresponding processing function through the route
            response = self.full_dispatch_request()
        except Exception as e:
            # Error handling. The default is the InternalServerError error error handling function. The client will see the server 500 exception
            error = e
            response = self.handle_exception(e)
        return response(environ, start_response)
    finally:
        if self.should_ignore_error(error):
            error = None
        # No matter whether an exception occurs in the processing, you need to pop the request out of the stack
        ctx.auto_pop(error)

The above code has only one purpose: to find the processing function and then call it. In addition to exception handling, we also saw content related to context (ctx.push() at the beginning and ctx.push() at the end) auto_ The logic of Pop () does not affect our understanding. We can ignore it now. There will be an article devoted to it later.

Keep looking back, full_ dsipatch_ The code of request is as follows:

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)

The core content of this code is dispatch_request, plus Requested hooks processing And error handling.

NOTE: self.dispatch_request() returns the returned result of the processing function (such as the string returned in the hello world example). finalize_request will convert it into a Response object.

In dispatch_ We saw preprocess before request_ Request, and then see finalize_request, which includes many hooks before and after request processing. These hooks include:

  • The hook function before the first request is processed through before_first_request definition
  • The hook function before each request is processed through before_request definition
  • The hook function after normal processing of each request, through after_request definition
  • Teardown to execute regardless of whether the request is abnormal or not_ Request hook function

dispatch_ What request needs to do is to find our handler function and return the result of the call, that is, the routing process. Let's talk about it in the next article!

Keywords: Python Back-end Flask

Added by zhaohongli on Tue, 04 Jan 2022 06:49:00 +0200