结合Django学习WSGI协议

之前在很多文章中说了WSGI协议,介绍了基本知识,可参考如下文章:

本文主要结合Django的内置web server来说一下。

有三个重要角色:

1、 WSGIServer ;

2、Django APP—WSGIHandler;

3、 WSGIRequestHandler:针对environ进行预处理和对接WSGIHandler。

我们都知道WSGI最重要的就是服务端要将environ,回调函数start_response两个必选参数传递给web应用,web应用进行响应,并执行回调函数,把相应结果一并传回给服务端,即web server。那在Django到底如何实现的呢?Django是如何将两者串联起来的呢?

一切都从下面代码开始:

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_cls = WSGIServer
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
    
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()

这段代码是执行runserver之后执行的函数,本端代码将WSGIServer和WSGIHander联系了起来。

此外,还有一点注意,当threading为True时,就使用多线程server。Django内置的server默认情况下是采用多线程的,如果不想用,需要在运行runserver命令时加上选项-nothreading。具体可看django management中的runserver模块。

很明显,set_app是加载应用,即将application加载进来;server_forever就是一直监听socket端口。

 

我们再先单独看一下服务端和应用。

服务端server:

class WSGIServer(HTTPServer):

    """BaseHTTPServer that implements the Python WSGI protocol"""

    application = None

    def server_bind(self):
        """Override server_bind to store the server name."""
        HTTPServer.server_bind(self)
        self.setup_environ()

    def setup_environ(self):
        # Set up base environment
        env = self.base_environ = {}
        env['SERVER_NAME'] = self.server_name
        env['GATEWAY_INTERFACE'] = 'CGI/1.1'
        env['SERVER_PORT'] = str(self.server_port)
        env['REMOTE_HOST']=''
        env['CONTENT_LENGTH']=''
        env['SCRIPT_NAME'] = ''

    def get_app(self):
        return self.application

    def set_app(self,application):
        self.application = application

看上面的代码看不出什么门道,所以还是要看父类,追本溯源,找到WSGIServer的父类包括HTTPServer,BaseServer。

BaseServer实现了相应方法:

class BaseServer:

  

    def __init__(self, server_address, RequestHandlerClass):
        """Constructor.  May be extended, do not override."""
        self.server_address = server_address
        self.RequestHandlerClass = RequestHandlerClass
        self.__is_shut_down = threading.Event()
        self.__shutdown_request = False

   
    def serve_forever(self, poll_interval=0.5):
        """Handle one request at a time until shutdown.

        try:
            while not self.__shutdown_request:
                # 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.
                r, w, e = _eintr_retry(select.select, [self], [], [],
                                       poll_interval)
                if self in r:
                    self._handle_request_noblock()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()

    

    def finish_request(self, request, client_address):
        """Finish one request by instantiating RequestHandlerClass."""
        self.RequestHandlerClass(request, client_address, self)

我把最重要的方法放上去了,其他代码删除了。server_forever执行后,最终会调用finish_request方法,会实例化一个RequestHandlerClass,看到这,应该断定,该类肯定有一个__init__方法。

这个类实际上就是上面run函数中的WSGIRequestHandler。

看下父类:

class BaseRequestHandler:

    
    def __init__(self, request, client_address, server):
        self.request = request
        self.client_address = client_address
        self.server = server
        self.setup()
        try:
            self.handle()
        finally:
            self.finish()

他会调用WSGIRequestHandler的handler方法,那我们再看一下WSGIRequestHandler的代码:


class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):

    def __init__(self, *args, **kwargs):
        self.style = color_style()
        super(WSGIRequestHandler, self).__init__(*args, **kwargs)


    def handle(self):
        """Copy of WSGIRequestHandler, but with different ServerHandler"""

        self.raw_requestline = self.rfile.readline(65537)
        if len(self.raw_requestline) > 65536:
            self.requestline = ''
            self.request_version = ''
            self.command = ''
            self.send_error(414)
            return

        if not self.parse_request():  # An error code has been sent, just exit
            return

        handler = ServerHandler(
            self.rfile, self.wfile, self.get_stderr(), self.get_environ()
        )
        handler.request_handler = self      # backpointer for logging
        handler.run(self.server.get_app())

看代码的最后一个语句,self.server.get_app()肯定是获得Django的app啊。看一下handler的run方法:

class BaseHandler:
    """Manage the invocation of a WSGI application"""

  

    def run(self, application):
        """Invoke the application"""
       ill be output to iterate over.
        try:
            self.setup_environ()
            self.result = application(self.environ, self.start_response)
            self.finish_response()
        except:
            pass

看到了没,漏出端倪了吧。

    self.result = application(self.environ, self.start_response)

这句话就是我们最想看到的啊,是WSGI协议的标准定义,application就是Web应用,Django中也就是WSGIHander,这个类肯定含有__call__方法。

class WSGIHandler(base.BaseHandler):
    initLock = Lock()
    request_class = WSGIRequest

    def __call__(self, environ, start_response):
        # Set up middleware if needed. We couldn't do this earlier, because
        # settings weren't available.
        

        try:
            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.output(header=''))))
        start_response(force_str(status), response_headers)
       
        return response

 

--------EOF---------
微信分享/微信扫码阅读