In the Web server and web framework where Python builds Web sites, we understand the concepts of Web servers, Web applications, and web frameworks. For Python, more and more web frameworks are available, limiting our choice of Web servers while giving us more choices. Similarly, Java with many Web frameworks, because of the presence of the servlet API, any application written by a Java WEB framework can run on any web Server.
The Python community, of course, needs a suite of APIs to fit WEB servers and applications, which is WSGI (Python Web server Gateway Interface), which is described in PEP 3333. In simple terms, WSGI is a bridge between Web servers and Web applications, from Web server to the original HTTP data, processed into a unified format to the Web application, on the other hand from the application/framework side of the business logic processing, generate response content to the server.
The detailed process for the Web server and framework to be coupled through WSGI is shown in the following illustration:
WSGI Server Adaptation
The specific explanations are as follows:
The application (Network framework) provides a callable object named application (the WSGI protocol does not specify how to implement this object). Each time the server receives a request from the HTTP client, it invokes the callable object application, passing a dictionary called Environ as an argument, and a callable object named Start_response. The framework/application generates an HTTP status code and an HTTP response header, which is then passed to Start_response for the server to save. In addition, the frame/application will also return the body of the response. The server combines the status code, response header, and response body into an HTTP response and returns it to the client (this step is not part of the WSGI protocol).
Look at the server side and the application side below to see how WSGI is suited.
Server-side
We know that each HTTP request made by a client (usually a browser) consists of a request line, a message header, and a request body, which contains the details of the request. Like what:
Method: Indicates the methods that are executed on resources identified by Request-uri, including Get,post et user-agent: Allow the client to tell the server about its operating system, browser, and other properties;
After the server receives the HTTP request from the client, the WSGI interface must unify the request fields for easy transmission to the Application server interface (in fact, to the framework). The data that the Web server delivers specifically to the application is detailed in the CGI (Common Gateway Interface, Universal Gateways), which are called CGI environment variables. WSGI follows the content of CGI environment variables, requiring the WEB server to create a dictionary to hold these environment variables (typically named Environ). In addition to the CGI-defined variables, environ must also save some WSGI-defined variables, as well as save some of the client system's environment variables, and refer to Environ Variables to see what the specific variables are.
The WSGI interface must then be environ to the application to handle, where WSGI the application provides a callable object application, and then the server invokes application to obtain the return value of the HTTP response body. When the server calls application, it needs to provide two variables, one is the variable dictionary environ mentioned earlier, the other is the callable object Start_response, which produces the state code and the response header, so we get a full HTTP response. The WEB server returns the response to the client, and a complete HTTP request-response process is complete.
Wsgiref Analysis
Python has a WEB server that implements the Wsgi interface, and in module Wsgiref, it's a reference implementation of a WSGI server written in pure Python, and we're going to make a simple analysis of its implementation. Let's start with a WEB server with the following code:
# Instantiate the server
httpd = make_server (
' localhost ', # The host name
8051, # A port number where to Wait for the "request
application #" Application object name, in the "Case a function" # Wait for
a Sin GLE request, serve it and quit
Then we take a Web server to receive a request, generate Environ, and then call application to process the request this line to analyze the call process of the source code, simplified as shown in the following figure:
WSGI Server Call Process
There are three main classes here, Wsgiserver,wsgirequesthandler,serverhandle. Wsgiserver is a Web server class that can provide Server_address (Ip:port) and Wsgirequesthandler classes to initialize to obtain a server object. The object listens for the port of the response, and after receiving an HTTP request, creates an instance of the RequestHandler class by Finish_request, generates an instance of the Handle class during initialization of the instance, and then calls its run (application) function. In this function, the application object provided by the application is invoked to generate the response.
The inheritance relationships of these three classes are shown in the following illustration:
WSGI class inheritance Diagram
Where TCPServer uses sockets to complete TCP communication, Httpserver is used to do HTTP level processing. Similarly, Streamrequesthandler to deal with stream Socket,basehttprequesthandler is used to deal with the HTTP level of content, which is not related to the WSGI interface, more of the WEB server's specific implementation, can be ignored.
Micro Server Instance
If the above wsgiref is too complex, the next step is to implement a small Web server that allows us to understand the implementation of the Web server-side WSGI interface. The code is excerpted from your own hands-on Development network server (ii), placed on the gist, the main structure is as follows:
Class Wsgiserver (object): # socket parameter address_family, Socket_type = socket.af_inet, socket.
Sock_stream request_queue_size = 1 def __init__ (self, server_address): # TCP Service-side initialization: Creating sockets, binding addresses, listening ports # getting server address, port
def set_app (self, application): # Get the application provided by the framework self.application = Application def serve_forever (self): # process TCP connections: Get request content, call processing function def handle_request (self): # Parse HTTP request, get environ, process request content, return HTTP response result env = Self.get_env Iron () result = Self.application (env, Self.start_response) self.finish_response (Result) def parse_request (self, TE
XT): # parsing HTTP request def get_environ (self): # parsing Environ parameters, here is an example where there are many parameters. env[' wsgi.url_scheme '] = ' http ' ... env[' request_method ' = Self.request_method # get ... return env def STA Rt_response (self, status, Response_headers, Exc_info=none): # Add response header, status code self.headers_set = [Status, Response_headers + server_headers] def finish_response (self, result): # return HTTP response Information server_addrESS = (HOST, PORT) = ', 8888 # Create a server instance def make_server (server_address, application): Server = Wsgiserver (server_add RESS) Server.set_app (application) return server
There are a lot of mature Web servers supporting WSGI, and Gunicorn is a pretty good one. It was born out of the Ruby Community's Unicorn and successfully ported to Python as a wsgi HTTP Server. Has the following advantages:
Easy to configure can automatically manage multiple worker processes select different background extension interfaces (sync, gevent, Tornado, etc.) application side (framework)
Rather than the server side, the application side (and the framework) is much simpler to do, and it only needs to provide a callable object (typically named application), which receives two parameters environ and start_response that are passed on the server side. The callable object here can be not only a function, but also a class (the second example below) or an instance of a __call__ method, so long as you can accept the preceding two arguments, and the return value can be iterated by the server.
Application specifically to do is based on the information provided in the Environ about HTTP requests, carry out a certain business processing, return an iterative object, the server side by iterating this object to obtain the body of the HTTP response. If there is no response body, you can return none.
At the same time, application also invokes the start_response provided by the server, generating the status code and Response headers for the HTTP response, as follows:
def start_response (self, status, Headers,exc_info=none):
Application needs to provide status: A string that represents the HTTP response status string and Response_headers: A list that contains tuples in the following form: (Header_name, Header_value), The headers used to represent the HTTP response. Exc_info is optional, and the server needs to return information to the browser when an error occurs.
So far, we can implement a simple application, as follows:
def simple_app (environ, start_response): "" "
simplest possible application function" ""
Hello_world = "HELLO world!\n "
status = ' OK '
response_headers = [(' Content-type ', ' Text/plain ')]
start_response (status, Response_headers)
Or use the class to implement the following.
Class Appclass: "" "
produce the same output, but using a class" "
def __init__ (self, Environ, start_response):
self.environ = environ
Self.start = start_response
def __iter__ (self):
...
Hello_world = "Hello world!\n"
Note that here the Appclass class itself is application, with Environ and start_response invocation (instantiation), which returns an instance object that itself is iterative in accordance with WSGI's requirements for application.
If you want to use an object of the Appclass class as a application, you must add a __call__ method to the class, accept the Environ and start_response as parameters, and return the iteration object as follows:
Class Appclass: "" "
produce the same output, but using an object" "
This section deals with some of the advanced features of Python, such as yield and magic method, which can be understood by reference to my summary of the Python language essentials.
The WSGI in the flask
Flask is a lightweight Python web framework that conforms to the WSGI specification requirements. Its original version is only more than 600 lines, relatively easy to understand. Let's look at the section on the WSGI interface in its original version.
def wsgi_app (self, Environ, start_response): "" "The
actual WSGI application.
This isn't implemented in ' __call__ ' So, middlewares can be applied:
App.wsgi_app = Mymiddleware (App.wsgi_app)
"" "With
Self.request_context (environ):
RV = self.preprocess_request ()
If RV = None:
RV = Self.dispatch_request ()
response = Self.make_response (RV)
response = self.process_response (response) Return
response (environ, start_response)
def __call__ (self, Environ, start_response): "" "
Shortcut For:attr: ' Wsgi_app ' "" "
The Wsgi_app here implements what we call the application function, the RV is the encapsulation of the request, response is the concrete function that the framework uses to handle the business logic. Here flask source code does not do too much explanation, interested can go to github download, and then check to the original version to view.
Middleware
The previous Flask Code WSGI_APP functions Note that the Application section is not implemented directly in __call__ in order to be able to use middleware. So why use middleware, and what is middleware?
Looking back at the previous Application/server end interface, for an HTTP request, the server side always calls a application for processing and returns the results of the application processing. This is enough to deal with the general scene, but not perfect, consider several of the following application scenarios:
For different requests (such as different URLs), the server needs to invoke different application, so how to choose which to call? In order to do load balancing or remoting, it is necessary to use the application running on other hosts on the network to do the processing; It is necessary to deal with the content returned by application before it can be used as HTTP response;
One common feature of these scenarios is that there are some essential actions that are not appropriate either on the server or on the application (framework) side. For the application side, these operations should be done by the server side, and for the server side, these operations should be done by the application side. In order to deal with this situation, the middleware is introduced.
Middleware is like a bridge between the application end and the service side to communicate on both sides. On the server side, the middleware behaves like an application end, and for the application, it behaves like a server side. As shown in the following illustration:
Middleware
Implementation of middleware
The Flask framework uses middleware in the initialization code of the Flask class:
The role here, like the adorner in Python, is to perform some of the contents of the Shareddatamiddleware before and after the Self.wsgi_app. Middleware does things that are similar to what the adorners in Python do. Shareddatamiddleware Middleware is provided by the Werkzeug Library to support site-hosted static content. In addition, there are dispatchermiddleware middleware to support the invocation of different application based on different requests, which can solve the problem in the previous scenario 1, 2.
Here's a look at the implementation of Dispatchermiddleware:
class Dispatchermiddleware (object): "" Allows one to mount Middlewares or applications I
n a WSGI application. This is useful if your want to combine multiple WSGI applications:: App = Dispatchermiddleware (app, {'/app2 ': app2
, '/APP3 ': app3}) "" "Def __init__ (self, app, mounts=none): Self.app = App Self.mounts = mounts or {} def __call__ (self, Environ, start_response): script = Environ.get (' path_info ', ') Path_info = ' while '/' in S Cript:if script in Self.mounts:app = Self.mounts[script] Break script, Last_item = Script.rsplit ('/', 1) path_info = '/%s%s '% (Last_item, path_info) Else:app = self.mounts.get (script, Self.app) Original_scrip T_name = Environ.get (' script_name ', ') environ[' script_name '] = original_script_name + SCRIPT environ[' PATH_INFO '] = Path_info return app (environ, start_response)
When initializing the middleware, you need to provide a mounts dictionary to specify the mapping relationship of different URL paths to application. So for a request, the middleware checks its path and then selects the appropriate application for processing.
The principle part about WSGI is basically over, the next one I will introduce the understanding of the flask framework.