WSGI agreement

First, clarify the following concepts: WSGI: WSGI is not a Server, Python module, framework, API, or any piece of software, but a specification that describes how the Web Server communicates with Web Applications. The specifications for the Server and application are detailed in PEP 3333. To implement WSGI, you must implement both Web Server and Web Application. The current Web frameworks running on WSGI include Bottle, Flask and Django. Uwsgi: As a communication protocol, it is the exclusive protocol of THE uWSGI server and is used to define the type of information transmitted. The first 4 bytes of each uWSGI packet are the description of the type of information transmitted, which is different from the WSGI protocol. This protocol is said to be 10 times faster than fcGI. UWSGI: a Web server that implements WSGI, uWSGI, HTTP, and so on.

WSGI protocol mainly consists of server and Application:

  • WSGI serverResponsible for receiving requests from the client, willrequestForwarded toapplicationThat will beapplicationThe returnedresponseReturn to the client;
  • WSGI applicationReceived by theserverforwardingrequest, process the request and return the result toserver.applicationCan include multiple stacks of middleware (middlewares), these middleware need to implement both server and application, so they can mediate between WSGI server and WSGI application: middleware acts as the application for the server, and middleware acts as the server for the application.

WSGI protocol actually defines a decoupling specification of server and application, that is, there can be multiple servers implementing WSGI Server and multiple frameworks implementing WSGI Application. You can then choose any combination of server and Application to implement your own Web application. For example, uWSGI and Gunicorn are servers that implement the WSGI Server protocol, while Django and Flask are Web frameworks that implement the WSGI Application protocol. They can be used together according to the actual situation of the project.


Wsgi. PNG – 22.9 kB

The Flask framework, like Django, has its own implementation of a simple WSGI Server, which is generally used for server debugging, and other WSGI servers are recommended for production environments.

Implementation of the WSGI protocol

Using Django as an example, take a look at the implementation process of WSGI.

django WSGI application

The WSGI Application should be implemented as a callable object, such as a function, method, class (including the ‘call’ method). Two parameters need to be received:

  • A dictionary, which can contain information about a client request as well as other information, can be thought of as the request context, commonly calledenvironment(The code is often abbreviated asenviron,env)
  • An application that sends HTTP response status (HTTP status), response headers (HTTP headers)

The callback function returns the response status and the response header to the server, along with the response body, which is iterable and contains multiple strings. Here is the implementation of an application in Django:

class WSGIHandler(base.BaseHandler): initLock = Lock() request_class = WSGIRequest def __call__(self, environ, start_response): If self._request_middleware is None: with self. InitLock: try: # Check that middleware is still uninitialized. if self._request_middleware is None: self.load_middleware() except: # Unload whatever middleware we got self._request_middleware = None raise set_script_prefix(get_script_name(environ)) # Request_started. Send (sender=self.__class__, environ=environ) request = self.request_class(environ) except UnicodeDecodeError: logger.warning('Bad Request (UnicodeDecodeError)', exc_info=sys.exc_info(), extra={'status_code': 400,}) response = http.HttpResponseBadRequest() else: response = self.get_response(request) response._handler_class = self.__class__ status = '%s %s' % (response.status_code,  response.reason_phrase) response_headers = [(str(k), str(v)) for k, v in response.items()] for c in response.cookies.values(): Response_headers. Append ((STR (' set-cookie '), STR (c. utput(header=')))) ')) Respont_response (force_str(status), response_headers) if getattr(response, 'file_to_stream', response_headers) None) is not None and environ.get('wsgi.file_wrapper'): response = environ['wsgi.file_wrapper'](response.file_to_stream) return responseCopy the code

It can be seen that the process of application includes:

  • Load all middleware and perform frame-related operations, set the current thread script prefix, send request start signal;
  • Handle requests, callsget_response()Method to process the current request. The main logic of this method is to passurlconfFind the correspondingviewandcallback, in order to execute variousmiddlewareandcallback.
  • Call byserverThe incomingstart_response()The method will respondheaderwithstatusBack to theserver.
  • Return the response body

django WSGI Server

It is responsible for getting the HTTP request and passing it to the WSGI Application, which processes the request and returns a response. Take a look at the implementation using Djangos built-in Server as an example. When running a Django project through RunServer, the following run method is called at startup, creating an instance of WSGIServer, and then calling its serve_forever() method to start the service.

def run(addr, port, wsgi_handler, ipv6=False, threading=False): server_address = (addr, port) if threading: httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {}) else: HTTPD = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6) if threading: httpd.daemon_threads = True httpd.set_app(wsgi_handler) httpd.serve_forever()Copy the code

The key classes and methods in the WSGI Server server process are represented below.


Xiong (2). The PNG – 93.7 kB

  • WSGIServer

    run()Method createsWSGIServerInstance, whose main function is to receive client requests and pass them toapplicationAnd thenapplicationThe returnedresponseReturn to the client.
    • Specified when creating the instanceHTTPThe request ofhandler:WSGIRequestHandlerclass
    • throughset_appandget_appMethod to set and getWSGIApplicationThe instancewsgi_handler
    • Call when processing an HTTP requesthandler_requestMethod is createdWSGIRequestHandlerInstance processing HTTP requests.
    • WSGIServerIn theget_requestMethods bysocketAccept request data
  • WSGIRequestHandler
    • byWSGIServerIn the callhandle_requestCreate instance when passed inrequest,cient_address,WSGIServerThree parameters,__init__The method will also call itself when instantiatedhandlemethods
    • handleMethod createsServerHandlerInstance, and then invoke itrunMethod to process the request
  • ServerHandler
    • WSGIRequestHandlerIn itshandleMethod callrunMethod, passed inself.server.get_app()Parameter, obtainWSGIApplication, and then calls the instance (__call__),response, which is passed instart_responseCallback, used to process the returnheaderandstatus.
    • throughapplicationTo obtainresponseLater, throughfinish_responsereturnresponse
  • WSGIHandler
    • WSGIIn the agreementapplication, receives two parameters,environThe dictionary contains information about the client request as well as other information that can be considered the request context,start_responseUsed to send backstatusandheaderThe callback function of

Although the WSGI Server above involves multiple class implementations and references, the principle is to call WSGIHandler, pass in the request parameters and the callback method start_response(), and return the response to the client.

django simple_server

Django’s simple_server.py module implements a simple HTTP server and provides a simple demo that can be run directly and display the environment variables involved in the request in the browser. This includes all the components of the entire HTTP request described above: ServerHandler, WSGIServer, WSGIRequestHandler, and a simplified version of WSGIApplication represented by Demo_app. Take a look at the process:

if __name__ == '__main__': Create WSGIServer instance from make_server demo_app httpd = make_server('', 8000, demo_app) sa = httpd.socket.getsockname() print("Serving HTTP on", sa[0], "port", sa[1], "..." ) import webbrowser webbrowser.open('http://localhost:8000/xyz? HTTPD. Handle_request () # serve one request, then exit httpd.server_close() def make_server( host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler ): """Create a new WSGI server listening on `host` and `port` for `app`""" server = server_class((host, port), Handler_class) server.set_app(app) return server # demo_app(environ,start_response): from io import StringIO stdout = StringIO() print("Hello world!" , file=stdout) print(file=stdout) h = sorted(environ.items()) for k,v in h: print(k,'=',repr(v), file=stdout) start_response("200 OK", [('Content-Type','text/plain;  charset=utf-8')]) return [stdout.getvalue().encode("utf-8")]Copy the code

Demo_app () represents a simple WSGI Application implementation that creates an instance of WSGIServer using the make_server() method and calls its handle_Request () method, which calls Demo_app () to handle the request, And eventually returns a response.

uWSGI

UWSGI aims to develop a complete solution for web applications that deploy distributed clusters. Primarily web oriented and standard services. Because of its extensibility, it can be extended indefinitely to support more platforms and languages. UWSGI is a Web server that implements THE WSGI protocol, uWSGI protocol, HTTP protocol, etc. The key features of uWSGI are:

  • Super fast performance
  • Low memory footprint
  • moreappmanagement
  • Detailed logging capabilities (available for analysisappPerformance and bottlenecks)
  • Highly customizable (memory size limit, restart after a certain number of times, etc.)

UWSGI server implements the server part based on the uWSGI protocol. We only need to specify the application address in the uWSGI configuration file, and uWSGI can directly communicate with the WSGI application in the application framework.

Nginx and UWSGI communicate with each other using Python WSGI