Previously, there was some confusion about the relationship between Nginx, WSGI (or uWSGI, uwsgi), and Flask (or Django). After consulting some materials, the relationships have been clarified. In summary, from the moment a client sends an HTTP request to when Flask processes it, the request passes through three layers: the web server layer, the WSGI layer, and the web framework layer. Each layer plays a different role, as briefly introduced below.
Web Server Layer
In a traditional client-server architecture, the request processing flow is: the client sends a request to the server, the server receives and processes the request, and then returns a response to the client. In this process, the server's role is:
- Receive the request
- Process the request
- Return a response
A web server is a special type of server whose main function is to receive HTTP requests and return responses. Common web servers include Nginx, Apache, and IIS. In the three-layer structure, the web server is the first to receive user requests and the last to return responses to users.
Web Framework Layer
The main purpose of a web framework is to facilitate the development of web applications. Dynamic HTTP request data is provided by the web framework layer. Common web frameworks include Flask and Django. Taking Flask as an example:
from flask import Flask
app = Flask(__name__)
@app.route('/hello')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
The above few lines create a web application object app. app listens on port 8080 on all IP addresses of the machine, accepting user connection requests. The HTTP protocol uses URLs to locate resources. The above program routes requests for the path /hello to the hello_world method, which returns the string 'Hello World!'. For users of the web framework, they don't need to care about how to receive HTTP requests, how to route requests to specific methods for processing, or how to return the response results to the user. In most cases, users of the web framework only need to focus on implementing business logic.
WSGI Layer
Understanding the HTTP protocol and HTML documents reveals that the essence of a web application is:
- The browser sends an HTTP request.
- The server receives the request and generates an HTML document.
- The server sends the HTML document as the body of an HTTP response to the browser.
- The browser receives the HTTP response, extracts the HTML document from the HTTP body, and displays it.
Therefore, the simplest web application is to save HTML in a file, use an existing HTTP server software to receive user requests, read the HTML from the file, and return it. Common static servers like Apache, Nginx, and Lighttpd do exactly this.
To generate HTML dynamically, you need to implement the above steps yourself. However, receiving HTTP requests, parsing them, and sending responses are tedious tasks. If we had to write all this low-level code ourselves, we would spend months reading the HTTP specification before even starting to write dynamic HTML.
A better approach is to have specialized server software handle the low-level code while we focus on generating HTML documents with Python. Since we don't want to deal with TCP connections, raw HTTP requests, and response formats, we need a unified interface that allows us to concentrate on writing web business logic using Python.
This interface is WSGI: Web Server Gateway Interface.
WSGI is not a server, nor an API for interacting with programs, nor actual code. WSGI is an interface specific to Python, defining the interface specification between web servers and web applications. As long as both the web server and web application comply with the WSGI protocol, they can be freely combined.
The following code demonstrates how a web server combines with a web application:
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b"Hello World"]
The application method is called by the web server. The parameters env and start_response are implemented and passed by the web server. env is a dictionary containing environment variables like HTTP_HOST, HOST_USER_AGENT, and SERVER_PROTOCOL. start_response is a method that accepts two parameters: status and response_headers. The main function of the application method is to set the HTTP response status code and headers like Content-Type, and to return the specific response content.
The code above is a complete WSGI application. When a WSGI-compliant web server receives a client request, it calls this application method. The WSGI layer does not need to know how env and start_response are implemented; it simply uses them as shown.
It's worth noting that WSGI is a protocol. Several similar terms should be distinguished:
uwsgi Like wsgi, it is also a protocol. The uWSGI server uses the uwsgi protocol. uWSGI A web server that implements both the uwsgi and WSGI protocols. Note that uWSGI is essentially a web server, residing in the web server layer of the three-layer structure described above. CGI Common Gateway Interface, not limited to Python, defines how a web server provides dynamic content to clients. For example, it specifies how clients pass parameters to the web server, how the web server passes parameters to the web application, and how the web application sends its output to the client, etc. In production environments, web applications no longer use CGI. CGI processes (like Python interpreters) are created for each request and discarded after use, resulting in low efficiency. WSGI emerged to replace CGI. At this point, we have essentially clarified the role of WSGI between web servers and web frameworks: WSGI acts as a link connnecting the web server and the web framework. Returning to the topic of this article, Nginx is a type of web server, and Flask is a web framework. Therefore, the roles of WSGI, Nginx, and Flask are self-evident.
Let's conclude this article with a conversation among Nginx, WSGI, and Flask.
Nginx: Hey, WSGI, I just received a request. I need you to make some preparations, and then Flask will handle it. WSGI: OK, Nginx. I will set up the environment variables and then pass the request to Flask for processing. Flask: Thanks, WSGI! Give me some time, and I will return the response for this request to you. WSSGI: Alright, I'll wait for you. Flask: Okay, I'm done. Here is the response result; please pass it to Nginx. WSGI: Good job! Nginx, here is the response result. I've returned it as requested. Nginx: Cool, I got it. I'll send the response back to the client. Great teamwork, everyone!