Django系列之启动入口源码分析
manage.py是啟動入口,在里面調用execute_from_command_line(sys.argv)方法
def execute_from_command_line(argv=None):"""Run a ManagementUtility."""utility = ManagementUtility(argv)utility.execute()ManagementUtility對象的execute()方法
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()方法
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_nameelse:# 關鍵代碼# 加載"django.contrib.staticfiles.management.commands.runserver"模塊中的Command類,# 并且實例化該類,klass實際上是一個Command類實例klass = load_command_class(app_name, subcommand)return klassklass是一個django.contrib.staticfiles.management.commands.runserver模塊中的Command類的實例對象,他的父類實際上是django.core.management.commands.runserver中的Command類。
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就是applicationhandler = 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模塊中
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):server_address = (addr, port)# threading默認為True,if threading:# 動態創建一個支持多線程的WSGIServer類,父類為ThreadingMixIn和原始的WSGIServerhttpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})else:httpd_cls = server_clshttpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)if threading:httpd.daemon_threads = Truehttpd.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()方法的調用。
注:每來一個請求都會實例化一個WSGIRequestHandler對象出來,然后這個對象來處理這個請求。
來看一下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 exitreturn# 實例化ServerHandler,django的WSGIHandler中傳入的start_response對象就是這個handlerhandler = 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類的實例對象,賦值給了實例屬性resultself.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_versionenv['wsgi.run_once'] = self.wsgi_run_onceenv['wsgi.url_scheme'] = self.get_scheme()env['wsgi.multithread'] = self.wsgi_multithreadenv['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.writedef 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 headersself.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的作用有三點:
ServerHandler的作用有三點:
實際上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 = WSGIRequestdef __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 responseapplication(environ, start_response)返回的是一個HTTPResponse類的實例對象response。在上面的ServerHandler的,run(appliction)方法可以看到,response被賦值給了ServerHandler的實例屬性result,最后把這個response中包含的數據發送給客戶端。
start_response()方法的作用,主要是將響應頭信息headers封裝在Header對象中,然后把這個Header對象作為start_response方法的持有者,即ServerHandler對象的一個屬性,最后在發送數據到客戶端時,調用ServerHandler對象的write(data)方法時,能直接取到自己的屬性headers來發送響應頭給客戶端。
轉載于:https://www.cnblogs.com/naralv/p/11110480.html
總結
以上是生活随笔為你收集整理的Django系列之启动入口源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux acl权限
- 下一篇: DS博客作业08--课程总结