manage.py
是啓動入口,在裏面調用execute_from_command_line(sys.argv)
方法html
def execute_from_command_line(argv=None): """Run a ManagementUtility.""" utility = ManagementUtility(argv) utility.execute()
ManagementUtility
對象的execute()
方法django
def execute(self): try: subcommand = self.argv[1] except IndexError: subcommand = 'help' # Display help if no arguments were given. if subcommand == 'help': ... elif subcommand == 'version' or self.argv[1:] == ['--version']: ... elif self.argv[1:] in (['--help'], ['-h']): ... else: # 關鍵代碼,傳入的subcommand='runserver'時,進入else分支 # 調用fetch_command()返回command對象,再調用對象的run_from_argv方法 self.fetch_command(subcommand).run_from_argv(self.argv)
fetch_command()
方法cookie
def fetch_command(self, subcommand): ... # Get commands outside of try block to prevent swallowing exceptions # commands是一個字典,key是subcommand,value是該subsommand對應的類所在的包的名稱 # 好比{'runserver': 'django.contrib.staticfiles, ...} commands = get_commands() try: # subcommand = "runserver" # app_name = "django.contrib.staticfiles" app_name = commands[subcommand] except KeyError: ... sys.exit(1) if isinstance(app_name, BaseCommand): klass = app_name else: # 關鍵代碼 # 加載"django.contrib.staticfiles.management.commands.runserver"模塊中的Command類, # 而且實例化該類,klass其實是一個Command類實例 klass = load_command_class(app_name, subcommand) return klass
klass
是一個django.contrib.staticfiles.management.commands.runserver
模塊中的Command
類的實例對象,他的父類其實是django.core.management.commands.runserver
中的Command類。session
run_from_argv()
方法是klass
對象的頂級父類中的方法。該方法中最終會調用django.core.management.commands.runserver
的Command類的handle()
方法。多線程
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'] # debug模式 if use_reloader: autoreload.main(self.inner_run, None, options) else: # 非debug模式,開啓服務 self.inner_run(None, **options) def inner_run(self, *args, **options): ... try: # 1. 先實例化WSGIHandler,獲得全局惟一的application對象,handler就是application handler = self.get_handler(*args, **options) # 2. 而後再開啓服務server_cls是django本身重寫的WSGIServer類,繼承自wsgiref的WSGIServer, # 重寫的不是關鍵方法,就當作是wsgiref的WSGIServer便可 run(self.addr, int(self.port), handler, ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls) except socket.error as e: ... os._exit(1) except KeyboardInterrupt: if shutdown_message: self.stdout.write(shutdown_message) sys.exit(0)
再看一下run()
方法,是一個獨立的函數在django.core.servers.basehttp
模塊中app
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer): server_address = (addr, port) # threading默認爲True, if threading: # 動態建立一個支持多線程的WSGIServer類,父類爲ThreadingMixIn和原始的WSGIServer httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {}) else: httpd_cls = server_cls httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6) if threading: httpd.daemon_threads = True httpd.set_app(wsgi_handler) httpd.serve_forever()
其中WSGIServer
是Django本身定義的一個類,繼承自wsgiref
模塊的simple_server.WSGIServer
類,繼承自標準庫http.server
模塊的HTTPServer
類,繼承自標準庫socketserver
模塊的TCPServer
類框架
WSGIRequestHandler
是Django本身定義的一個類,繼承自wsgiref
模塊的simple_server.WSGIRequestHandler
類,它重寫了父類的handle()方法
這個類被做爲參數傳入到WSGIServer
中,等到請求到來時,會觸發WSGIRequestHandler
類的實例化,在它實例化時會觸發handle()
方法的調用。socket
注:每來一個請求都會實例化一個
WSGIRequestHandler
對象出來,而後這個對象來處理這個請求。ide
來看一下django自定義的WSGIRequestHandler
這個類函數
class WSGIRequestHandler(simple_server.WSGIRequestHandler): protocol_version = 'HTTP/1.1' ... def get_environ(self): for k, v in self.headers.items(): if '_' in k: del self.headers[k] # 關鍵在於調用了父類的get_environ()方法,wsgiref模塊中的WSGIRequestHandler類的get_eniron()方法構造了environ字典 return super().get_environ() # handle()方法在WSGIRequestHandler的__init__()方法中被調用 def handle(self): """Copy of WSGIRequestHandler.handle() but with different ServerHandler""" # raw_requestline是客戶端發來的請求行數據,讀取請求行信息,其實這裏讀取的並不單單是請求行,而是 # 讀取了65537字節的數據到rfile這個緩衝輸入流中,在parse_request()方法中都會對這個raw_requestline # 信息用"\r\n"來分割,取出第一行,也就是指取出請求行的信息。 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 # parse_request()方法解析socket對象收到的http請求報文的請求行信息,賦值給本身的屬性 if not self.parse_request(): # An error code has been sent, just exit return # 實例化ServerHandler,django的WSGIHandler中傳入的start_response對象就是這個handler handler = ServerHandler( self.rfile, self.wfile, self.get_stderr(), self.get_environ() ) handler.request_handler = self # backpointer for logging # 在run()方法是調用application(environ, start_reponse)的 handler.run(self.server.get_app())
ServerHandler
的父類是wsgiref模塊的BaseHandler
,它的做用就是用來調用application(environ, start_response)
的,它在WSGIRequestHandler
的handle()
方法中被實例化,同時把WSGIRequestHandler
對象的實例屬性rfile
,wfile
,environ
做爲初始化參數傳入,此時ServerHandler
對象也持有這三個屬性。而後調用run(application)
來調用application
。
class BaseHandler: ... def run(self, application): """調用application""" try: self.setup_environ() # 調用application(),返回一個response對象,在django中是HTTPResponse類的實例對象,賦值給了實例屬性result self.result = application(self.environ, self.start_response) # 處理response對象中的數據發送到客戶端 self.finish_response() except: try: self.handle_error() except: # If we get an error handling an error, just give up already! self.close() raise # ...and let the actual server figure it out. def setup_environ(self): """Set up the environment for one request""" env = self.environ = self.os_environ.copy() self.add_cgi_vars() # 給environ字典添加其餘的key:value,這些字段是PEP333要求必須添加的 env['wsgi.input'] = self.get_stdin() env['wsgi.errors'] = self.get_stderr() env['wsgi.version'] = self.wsgi_version env['wsgi.run_once'] = self.wsgi_run_once env['wsgi.url_scheme'] = self.get_scheme() env['wsgi.multithread'] = self.wsgi_multithread env['wsgi.multiprocess'] = self.wsgi_multiprocess ... def finish_response(self): """處理application返回的result,是一個可迭代的response對象""" try: if not self.result_is_file() or not self.sendfile(): # response是一個可迭代對象,實現了__iter__()方法,返回response中保存的數據內容,data是html內容,不包含請求頭的內容 for data in self.result: self.write(data) self.finish_content() finally: self.close() def start_response(self, status, headers,exc_info=None): """'start_response()' callable as specified by PEP 3333""" ... self.status = status # 這個方法最重要的做用是把響應頭信息封裝在headers中,做爲本身的實例屬性保存起來 self.headers = self.headers_class(headers) ... return self.write def write(self, data): """'write()' callable as specified by PEP 3333""" ... if not self.status: ... # headers_sent初始值爲False,用於標記響應頭是否發送 elif not self.headers_sent: # Before the first output, send the stored headers self.bytes_sent = len(data) # make sure we know content-length # 1.先把響應狀態行+響應頭經過wfile.write()方法發送給客戶端,wfile就是一個跟socket關聯的輸出IO流對象 self.send_headers() else: self.bytes_sent += len(data) # XXX check Content-Length and truncate if too many bytes written? # 2. 再把響應體data經過wfile.write()方法發送到客戶端 self._write(data) # 這個方法沒用 self._flush()
總結:
WSGIRequestHandler
的做用有三點:
socket
對象分別封裝成rfile
和wfile
對象,他們都是帶緩衝的IO流對象,用於接受和發送數據給客戶端。get_environ()
方法,用來構造environ
字典handler()
方法,將BaseHandler
的子類實例化,同時傳入rfile
,wfile
,environ
參數。ServerHandler
的做用有三點:
WSGIRequestHandler
對象的rfile
,wfile
,environ
做爲初始化參數run(application)
來調用application(environ, start_response)
。finish_response()
來處理application(environ, start_response)
返回響應對象response
,迭代response
對象中存放的數據發送給客戶端。實際上ServerHandler
對象write(data)
方法中進行了3次socket.sendall()
調用,分別是:
socket.sendall(status_line)
響應狀態行socket.sendall(headers)
響應頭socket.sendall(body)
響應體WSGIRequestHandler
經過本身的handle()
方法與ServerHandler
對象產生了關聯。而ServerHandler
對象在本身的run(application)
方法中調用application(environ, start_response)
,而application(environ, start_response)
又是django框架處理請求的入口,整個django框架處理請求的邏輯就在application(environ, start_response)
內部去實現了。
application是一個WSGIHandler
類的實例化對象,application(environ, start_response)
調用其實是調用了該對象的__call__(environ, start_response)
方法。
class WSGIHandler(base.BaseHandler): # WSGIRequest類是django本身提供的請求類,實例化以後就是request對象 request_class = WSGIRequest def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 加載中間件實例到內存中,此時中間件實例的各類方法已經被包裹在 _get_response() 方法的先後了 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) # 實例化WSGIRequest,獲得request對象,request對象中此時並無session屬性,而是在中間件加載後, # 請求進入session中間件時,在session中間件的process_request()方法中動態添加的session屬性 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 = list(response.items()) for c in response.cookies.values(): response_headers.append(('Set-Cookie', c.output(header=''))) 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
application(environ, start_response)
返回的是一個HTTPResponse
類的實例對象response
。在上面的ServerHandler
的,run(appliction)
方法能夠看到,response
被賦值給了ServerHandler
的實例屬性result
,最後把這個response
中包含的數據發送給客戶端。
start_response()
方法的做用,主要是將響應頭信息headers封裝在Header對象中,而後把這個Header對象做爲start_response
方法的持有者,即ServerHandler
對象的一個屬性,最後在發送數據到客戶端時,調用ServerHandler
對象的write(data)
方法時,能直接取到本身的屬性headers來發送響應頭給客戶端。