结合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
微信分享/微信扫码阅读