Django 源码阅读(5):从请求到响应,Django是怎么做的

设置请求句柄

  • 接着第一节继续,显然runserver实现了自己的handle函数。handle 首先会做一些设置参数的错误检查,然后进入run函数,判断能否自动重载后就执行inner_run,可以重点分析下它。
#django\core\management\commands\runserver.py
    def handle(self, *args, **options):
        ...
        self.run(**options)

    def run(self, **options):
        """Run the server, using the autoreloader if needed."""
        use_reloader = options['use_reloader']

        if use_reloader:
            autoreload.run_with_reloader(self.inner_run, **options)
        else:
            self.inner_run(None, **options)
            
    def inner_run(self, *args, **options):
        # If an exception was silenced in ManagementUtility.execute in order
        # to be raised in the child process, raise it now.
        autoreload.raise_last_exception()

        threading = options['use_threading']
        # 'shutdown_message' is a stealth option.
        shutdown_message = options.get('shutdown_message', '')
        quit_command = 'CTRL-BREAK' if sys.platform == 'win32' else 'CONTROL-C'

        self.stdout.write("Performing system checks...\n\n")
        self.check(display_num_errors=True)
        ...

        try:
            handler = self.get_handler(*args, **options)
            run(self.addr, int(self.port), handler,
                ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)
        except socket.error as e:
            # Use helpful error messages instead of ugly tracebacks.
            ERRORS = {
                errno.EACCES: "You don't have permission to access that port.",
                errno.EADDRINUSE: "That port is already in use.",
                errno.EADDRNOTAVAIL: "That IP address can't be assigned to.",
            }
           ...
  • inner_run 除了熟悉的信息输出外,重要的是这个两个句柄
	handler = self.get_handler(*args, **options)
	run(self.addr, int(self.port), handler,
	    ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)
  • get_handler 函数最终会返回一个 WSGIHandler 的实例。WSGIHandler 类实现了 def __call__(self, environ, start_response) , 使它本身能够成为 WSGI 中的应用程序, 并且实现 __call__ 能让类的行为跟函数一样。
def get_handler(self, *args, **options):
    """Return the default WSGI handler for the runner."""
    return get_internal_wsgi_application()
# core\servers\basehttp.py
def get_internal_wsgi_application():
    """
    Load and return the WSGI application as configured by the user in
    ``settings.WSGI_APPLICATION``. With the default ``startproject`` layout,
    this will be the ``application`` object in ``projectname/wsgi.py``.

    This function, and the ``WSGI_APPLICATION`` setting itself, are only useful
    for Django's internal server (runserver); external WSGI servers should just
    be configured to point to the correct application object directly.

    If settings.WSGI_APPLICATION is not set (is ``None``), return
    whatever ``django.core.wsgi.get_wsgi_application`` returns.
    """
    from django.conf import settings
    app_path = getattr(settings, 'WSGI_APPLICATION')
    if app_path is None:
        return get_wsgi_application()
        
# core\wsgi.py
def get_wsgi_application():
    """
    The public interface to Django's WSGI support. Return a WSGI callable.
    Avoids making django.core.handlers.WSGIHandler a public API, in case the
    internal WSGI implementation changes or moves in the future.
    """
    django.setup(set_prefix=False)
    return WSGIHandler()
    
#core\handlers\wsgi.py
class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.load_middleware()

    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)
        request = self.request_class(environ)
        response = self.get_response(request)

        response._handler_class = self.__class__

        status = '%d %s' % (response.status_code, response.reason_phrase)
        response_headers = [
            *response.items(),
            *(('Set-Cookie', c.output(header='')) for c in response.cookies.values()),
        ]
        start_response(status, response_headers)
        if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
            response = environ['wsgi.file_wrapper'](response.file_to_stream)
        return response
  • 接着往下看 run(self.addr, int(self.port), handler, ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls) 函数。一个标准的 WGSI 实现,httpd_cls 是 WSGIServer 类,最终的实例化方法在 socketserver.py 中的 父类TCPServer 和 BaseServer 中。包括初始化线程,初始化网络句柄,像下面的 __is_shut_down 和 __shutdown_request 都是在其中初始化的。
    在这里插入图片描述
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
    server_address = (addr, port)
    if threading:
        httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
    else:
        httpd_cls = server_cls
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
    if threading:
        # ThreadingMixIn.daemon_threads indicates how threads will behave on an
        # abrupt shutdown; like quitting the server by the user or restarting
        # by the auto-reloader. True means the server will not wait for thread
        # termination before it quits. This will make auto-reloader faster
        # and will prevent the need to kill the server manually if a thread
        # isn't terminating correctly.
        httpd.daemon_threads = True
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()

处理请求

  • 当发现有请求后,就调用 _handle_request_noblock 进行处理:
# C:\Python36\Lib\socketserver.py
    def serve_forever(self, poll_interval=0.5):
        """Handle one request at a time until shutdown.
		处理一个 http 请求直到关闭
        Polls for shutdown every poll_interval seconds. Ignores
        self.timeout. If you need to do periodic tasks, do them in
        another thread.
        """
        # __is_shut_down为一个初始化的threading.Event()的句柄,用于线程间通信,clear()将标识设置为false
        self.__is_shut_down.clear()
        ...

		        while not self.__shutdown_request:
		        	# 一个封装好了的select函数,超时时间 0.5 s
		            ready = selector.select(poll_interval)
		            if ready:
		            	# 调用 _handle_request_noblock 做处理:
		                self._handle_request_noblock()
         ...
         finally:
            self.__shutdown_request = False
            #set将标识重置为true
            self.__is_shut_down.set() 
  • _handle_request_noblock
    def _handle_request_noblock(self):
        """Handle one request, without blocking.

        I assume that selector.select() has returned that the socket is
        readable before this function was called, so there should be no risk of
        blocking in get_request().
        """
        try:
        # 返回请求句柄,客户端地址,get_request()中调用了self.socket.accept()来实现客户端的连接
            request, client_address = self.get_request()
        except OSError:
            return
        # 验证请求合法性
        if self.verify_request(request, client_address):
            try:
            # 真正的处理连接请求的地方,调用了self.process_request(request, client_address)
                self.process_request(request, client_address)
            ...
  • 还记得前面的WSGIRequestHandler(httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6))吗? 而 finish_request 函数返回的就是 django.core.servers.basehttp.WSGIRequestHandler 的实例
    def process_request(self, request, client_address):
        """Call finish_request.

        Overridden by ForkingMixIn and ThreadingMixIn.

        """
        self.finish_request(request, client_address)
        self.shutdown_request(request)
        
    def finish_request(self, request, client_address):
        """Finish one request by instantiating RequestHandlerClass."""
        self.RequestHandlerClass(request, client_address, self)
  • 其父类 BaseHTTPRequestHandler 类中有对 http 包解包的过程,从最终的基类 BaseRequestHandler 初始化而来:在这里插入图片描述
class BaseRequestHandler:

    """Base class for request handler classes.

    This class is instantiated for each request to be handled.  The
    constructor sets the instance variables request, client_address
    and server, and then calls the handle() method.  To implement a
    specific service, all you need to do is to derive a class which
    defines a handle() method.

    The handle() method can find the request as self.request, the
    client address as self.client_address, and the server (in case it
    needs access to per-server information) as self.server.  Since a
    separate instance is created for each request, the handle() method
    can define other arbitrary instance variables.

    """

    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()

    def setup(self):
        pass

    def handle(self):
        pass

    def finish(self):
        pass

返回响应

  • 终于来到最终的应答阶段啦,从文档可以看出,会使用回调 handle()函数,也就是子类 WSGIRequestHandler 覆盖的方法,而handle内部实际上是 handle_one_request 在实现真正的功能。
    def handle(self):
        self.close_connection = True
        self.handle_one_request()
        while not self.close_connection:
            self.handle_one_request()
        ...

    def handle_one_request(self):
        """Copy of WSGIRequestHandler.handle() 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
		#传入的参数,读,写,错误,环境变量,ServerHandler继承自SimpleHandler
        handler = ServerHandler(
            self.rfile, self.wfile, self.get_stderr(), self.get_environ()
        )
        handler.request_handler = self      # backpointer for logging & connection closing
        handler.run(self.server.get_app())
        
# 在父类SimpleHandler中进行了初始化,并且默认打开了多进程选项
class SimpleHandler(BaseHandler):
    """
    处理程序子类用于同步HTTP / 1.0源服务器,并在给定正确输入的情况下处理发送整个响应输出。
    Usage::
        handler = SimpleHandler(
            inp,out,err,env, multithread=False, multiprocess=True
        )
        handler.run(app)"""

    def __init__(self,stdin,stdout,stderr,environ,
        multithread=True, multiprocess=False
    ):
        self.stdin = stdin
        self.stdout = stdout
        self.stderr = stderr
        self.base_env = environ
        self.wsgi_multithread = multithread
        self.wsgi_multiprocess = multiprocess
  • handler.run(self.server.get_app()) 中就是调用之前设置(httpd.set_app(wsgi_handler))句柄的 WSGIHandler 类,利用回调函数返回响应。
    def run(self, application):
        """Invoke the application"""
        # Note to self: don't move the close()!  Asynchronous servers shouldn't
        # call close() from finish_response(), so if you close() anywhere but
        # the double-error branch here, you'll break asynchronous servers by
        # prematurely closing.  Async servers must return from 'run()' without
        # closing if there might still be output to iterate over.
        try:
            self.setup_environ()
            self.result = application(self.environ, self.start_response)
            self.finish_response()
        ...
#core\handlers\wsgi.py
class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest
    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)
        request = self.request_class(environ)
        response = self.get_response(request)
        ...
        return response
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值