Django Launch and Request Flow

Django Launch and Request Flow

Basic information

  • Django version: 2.1

Django launch flow

PEP 3333

  • PEP 3333
    • 制定Web Server與Python web applications/frameworks的介面標準
  • 角色
    • Web Server/Gatway
      • 呼叫Application/Framework的callable實例,並傳入environ, start_response兩個參數
        • environ:
          • Type: Dict
          • 當前環境的上下文管理變數,為Dict type
        • start_response:
          • Type: Callable
          • 接收两个参数, 分别是响应的状态码
          • 返回Response Body
    • Application/Framework
      • 需要實作一個callble的實例,包含函數、方法、類,或帶有__call__的方法
    • Middleware
      • 對上級的Web Server或Middleware扮演Application的角色
      • 對下級的Application或Middleware扮演Server的角色

Interaction Diagram

Explanation

  • manage.py runserver
    • 此為每個Django Application的入口,在建立Django Project時,會自動產生
    • 重點步驟
      • 指定DJANGO_SETTINGS_MODUL,此為重要配置,Django Project相關配置皆使用該文件設定
      • 使用django.core.management的execute_from_command_line將參數傳遞至Django內部
  • management @django/core/management/init.py
    • 初始化ManagementUtility,並呼叫其execute方法
  • utility: ManagementUtility @django/core/management/init.py
    • execute方法
      • 判斷argv中的輸入,執行對應的動作,現在參數為”runserver”
      • self.fetch_command(subcommand).run_from_argv(self.argv)
        • fetch_command(subcommand)取得Command
  • Command @django/core/management/commands/runserver.py

    • run_from_argv(self.argv)
      • run_from_argv在Command的父類 @django/core/management/base.py #BaseCommand
    • execute(self, args, *options) #BaseCommand
    • handle(self, args, *options) #Command
    • run(self, **options) #Command
    • inner_run(self, args, *options) #Command

      1
      2
      3
      4
      5
      6
      7
      8
      9
      def inner_run(self, *args, **options):
      # ...
      try:
      # 取得WSGIHandler @django/core/handlers/wsgi.py
      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:
      # ...
  • run @django/core/servers/basehttp.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
    # ...
    # httpd_cls是WSGIServer @django/core/servers/basehttp.py
    # WSGIRequestHandler @django/core/servers/basehttp.py
    # WSGIRequestHandler最中會指定給WSGIServer的父類BaseServer的self.RequestHandlerClass變數
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
    # ...
    # wsgi_handler由上一步傳入的WSGIHandler @django/core/handlers/wsgi.py
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()
  • httpd.set_app

    1
    2
    def set_app(self,application):
    self.application = application
    • application會在之後處理HTTP request時候使用
  • httpd.serve_forever
    • 父類 BaseServer中的方法
    • 不斷接收與處理HTTP request,直到接收到shutdown信號

Djagno with PEP3333

  • Web Server/Gatway <-> WSGIServer
  • Application/Framework <-> WSGIHandler

Django Request and Response Flow

Interaction Diagram

Explanation

  • HTTP request

    • 當有HTTP/HTTPS request到達時,WSGIServer的serve_forever方法中的which loop會透過_handle_request_noblock處理request,之後調用內部的process_request,finish_request方法
    • 上述方法皆在WSGIServer的父類BaseServer中

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

    • 在finish_request方法中調用了self.RequestHandlerClass
    • RequestHandlerClass變量是run @django/core/servers/basehttp.py時傳入,Class為WSGIRequestHandler @django/core/servers/basehttp.py
    • WSGIRequestHandler在其父類BaseRequestHandler的int方法中呼叫handle方法

      1
      2
      3
      4
      5
      6
      7
      class BaseRequestHandler:
      def __init__(self, request, client_address, server):
      # ...
      try:
      self.handle()
      finally:
      # ...
    • handle方法實作在WSGIRequestHandler內,並呼叫自身的handle_one_request

      1
      2
      3
      4
      5
      6
      7
      8
      class WSGIRequestHandler(simple_server.WSGIRequestHandler):
      def handle(self):
      # ...
      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())
  • ServerHandler

    • run方法在其父類BaseHandler中

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      class BaseHandler:
      def run(self, application):
      # ...
      try:
      self.setup_environ()
      self.result = application(self.environ, self.start_response)
      self.finish_response()
      except:
      # ...
      def finish_response(self):
      try:
      if not self.result_is_file() or not self.sendfile():
      for data in self.result:
      self.write(data)
      self.finish_content()
      finally:
      self.close()
    • application(self.environ, self.start_response)

      • application為run方法的參數,使用WSGIRequestHandler中的self.server.get_app()取得
    • self.server.get_app()
      • self.server是在WSGIRequestHandler初始化時,其父類的參數
      • 參數是在WSGIServer中,使用self傳入,故self.server就是WSGIServer object
      • get_app方法對應WSGIServer的set_app方法,是在run @django/core/servers/basehttp.py 傳入,對應的是WSGIHandler
      • 故self.server.get_app()回傳的就是WSGIHandler class
  • WSGIHandler @django/core/handlers/wsgi.py

    • 在application(self.environ, self.start_response)會呼叫init
      • 載入middleware
    • 在finish_response方法中的self.result會呼叫WSGIHandler的call

      • 開始處理request,根據順序走訪middleware與開發者定義View,最終回傳response
      • call是根據PEP3333的Application規定實作,讓WSGIHandler成為callable,並且接收environ, start_response 2個參數
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      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)
      # ...
      return response
      class BaseHandler:
      def get_response(self, request):
      """Return an HttpResponse object for the given HttpRequest."""
      # Setup default url resolver for this thread
      set_urlconf(settings.ROOT_URLCONF)
      response = self._middleware_chain(request)
      response._closable_objects.append(request)
      if response.status_code >= 400:
      log_response(
      '%s: %s', response.reason_phrase, request.path,
      response=response,
      request=request,
      )
      return response