djangorunserver启动和处理http流程分析
前置
django manager.py runserver 0.0.0.0:80到底干了啥?
以下涉及到wsgi server,wsgi application。
runserver入口
命令行执行manager.py runserver最终会执行到下面的handle函数处
# django/core/management/commands/runserver.py
class Command(BaseCommand):
help = "Starts a lightweight Web server for development."
......
def handle(self, *args, **options):
...... # 上面做一些配置检查,环境准备等,忽略细节
self.run(**options) # 1. 重点关注此处,调用了run函数
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) # 2. 重点关注此处
def inner_run(self, *args, **options):
...... # 忽略细节
try:
handler = self.get_handler(*args, **options) # 3. 重点关注此处,这里其实就返回的wsgi application对象
run(self.addr, int(self.port), handler,
ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls) # 4. run函数启动一个web服务
except OSError as e:
...... # 忽略细节
inner_run里的get_handler实际返回的是wsgi application对象,调用关系见下,注意下面已标序
# django/core/management/commands/runserver.py
def get_handler(self, *args, **options):
"""Return the default WSGI handler for the runner."""
return get_internal_wsgi_application() # 3.1 重点关注此处
# django/core/servers/basehttp.py
def get_internal_wsgi_application(): # 3.1
from django.conf import settings
app_path = getattr(settings, "WSGI_APPLICATION")
if app_path is None:
return get_wsgi_application() # 3.2 重点关注此处
try:
return import_string(app_path)
except ImportError as err:
raise ImproperlyConfigured(
"WSGI application "%s" could not be loaded; "
"Error importing module." % app_path
) from err
# django/core/wsgi.py
def get_wsgi_application(): # 3.2
django.setup(set_prefix=False) # 设置django环境
return WSGIHandler() # 3.3 重点关注此处
# django/core/handlers/wsgi.py
class WSGIHandler(base.BaseHandler): # 3.3
request_class = WSGIRequest
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_middleware()
def __call__(self, environ, start_response): # 3.3 返回wsgi规范的wsgi application可调用对象
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) # 调用wsgi server端start_response函数处理返回状态和返回头部信息
......
return response # 返回可迭代的对象
可见最终会返回一个符合wsgi标准的application对象。
run server
之后,inner_run里的run函数会启动一个web server
# django/core/servers/basehttp.py
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:
httpd.daemon_threads = True
httpd.set_app(wsgi_handler) # 上面获取的wsgi application传到这里
httpd.serve_forever() # 会启动一个基于select模型的socket web server
httpd.serve_forever()会启动一个web server处理http请求和返回
httpd默认情况下就是一个WSGIServer,我们看下
# django/core/servers/basehttp.py
from wsgiref import simple_server
class WSGIServer(simple_server.WSGIServer):
"""BaseHTTPServer that implements the Python WSGI protocol"""
request_queue_size = 10
def __init__(self, *args, ipv6=False, allow_reuse_address=True, **kwargs):
if ipv6:
self.address_family = socket.AF_INET6
self.allow_reuse_address = allow_reuse_address
super().__init__(*args, **kwargs)
def handle_error(self, request, client_address):
if is_broken_pipe_error():
logger.info("- Broken pipe from %s
", client_address)
else:
super().handle_error(request, client_address)
其继承于simple_server.WSGIServer,来自python实现的一个简版wsgi server,暂不做深入分析。由于下面处理请求会用到WSGIRequestHandler,这里只讲一个WSGIRequestHandler的传递
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
最终会实例化到这个属性里self.RequestHandlerClass
# python3.7/sockerserver.py
class BaseServer:
timeout = None
def __init__(self, server_address, RequestHandlerClass):
"""Constructor. May be extended, do not override."""
self.server_address = server_address
self.RequestHandlerClass = RequestHandlerClass # 把WSGIRequestHandler传递到这里,最终在处理request请求的时候会调用到
self.__is_shut_down = threading.Event()
self.__shutdown_request = False
至此一个启动完成,接下来看下一个http请求过来到返回的过程。
HTTP请求
上面看到httpd.serve_forever()这里,那么处理请求的入口也肯定在这里。
process_request
# python3.7/socketserver.py
class BaseServer:
...... # 忽略其他细节
def serve_forever(self, poll_interval=0.5):
self.__is_shut_down.clear()
try:
with _ServerSelector() as selector:
selector.register(self, selectors.EVENT_READ)
while not self.__shutdown_request: # 这里开启死循环监听请求
ready = selector.select(poll_interval) # select模型监听
# bpo-35017: shutdown() called during select(), exit immediately.
if self.__shutdown_request:
break
if ready:
self._handle_request_noblock() # 1. 重点关注这里,请求入口
self.service_actions()
finally:
self.__shutdown_request = False
self.__is_shut_down.set()
........
def _handle_request_noblock(self): # 1
try:
request, client_address = self.get_request()
except OSError:
return
if self.verify_request(request, client_address):
try:
self.process_request(request, client_address) # 2. 重点关注这里,处理request
except Exception:
self.handle_error(request, client_address)
self.shutdown_request(request)
except:
self.shutdown_request(request)
raise
else:
self.shutdown_request(request)
.......
def process_request(self, request, client_address): # 2
"""Call finish_request.
Overridden by ForkingMixIn and ThreadingMixIn.
"""
self.finish_request(request, client_address) # 3. 重点关注这里
self.shutdown_request(request)
......
def finish_request(self, request, client_address): # 3
"""Finish one request by instantiating RequestHandlerClass."""
self.RequestHandlerClass(request, client_address, self) # 4. 看到了上面提及的self.RequestHandlerClass了
看上面代码启动了一个循环(除非来了关闭请求)来处理request,接下来的流程是:
self._handle_request_noblock -> self.process_request -> self.finish_request -> self.RequestHandlerClass
到这里就有点对上了,这里的self.RequestHandlerClass就是上面httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)里的参数WSGIRequestHandler,但这里为啥只是实例化了WSGIRequestHandler,我们继续看,由WSGIRequestHandler追踪__init__方法,发现最终的父类里有这段代码
# python3.7/socketserver.py
class BaseRequestHandler:
def __init__(self, request, client_address, server): # 注意这里init方法处理了整个调用过程,具体的setup,handle,finish由各自的子类实现
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
可见这里__init__方法处理了整个调用过程,具体的setup,handle,finish由各自的子类实现。再反过头来看WSGIRequestHandler的handle方法
# django/core/servers/basehttp.py
class WSGIRequestHandler(simple_server.WSGIRequestHandler):
protocol_version = "HTTP/1.1"
......
def handle(self):
self.close_connection = True
self.handle_one_request() # 1. 处理request请求
while not self.close_connection:
self.handle_one_request()
try:
self.connection.shutdown(socket.SHUT_WR)
except (AttributeError, OSError):
pass
......
def handle_one_request(self): # 1
"""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
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()) # 2. 调用ServerHandler里的run函数,传参就是wsgi.py里的WSGIHandler,也就是提供的wsgi application
handle函数里主要调用顺序: handle->handle_one_request->ServerHandler.run。 ServerHandler.run源码如下:
# python3.7/wsgiref/handlers.py
class BaseHandler:
def run(self, application):
"""Invoke the application"""
try:
self.setup_environ()
self.result = application(self.environ, self.start_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.
至此,我们看到了熟悉的 application(self.environ, self.start_response),这里才是真正执行我们代码写的业务逻辑的地方,前面都是环境的准备。具体application逻辑在django连接池试验里有描述。
finish_response
再看下self.finish_response
# python3.7/wsgiref/handlers.py
class BaseHandler:
# 省略细节
def finish_response(self): # 1
"""Send any iterable data, then close self and the iterable
Subclasses intended for use in asynchronous servers will
want to redefine this method, such that it sets up callbacks
in the event loop to iterate over the data, and to call
"self.close()" once the response is finished.
"""
try:
if not self.result_is_file() or not self.sendfile():
for data in self.result:
self.write(data) # 这里主要是往socket写返回数据
self.finish_content() # 确保头部信息和内容已经发送
finally:
self.close() # 2 重点关注下这里
......
def close(self): # 2
"""Close the iterable (if needed) and reset all instance vars
Subclasses may want to also drop the client connection.
"""
try:
if hasattr(self.result,"close"):
self.result.close() #3. self.result即为前面的wsgi application返回的response,如果有提供close方法的话,这里调用。比如django关闭db的连接就是放在这个close里的
finally:
self.result = self.headers = self.status = self.environ = None
self.bytes_sent = 0; self.headers_sent = False
self.result即为前面的wsgi application返回的response,如果有提供close方法的话,这里调用。比如django关闭db的连接就是放在这个close里的
结束
最终走完了整个HTTP请求流程。fuuuck
以上是 djangorunserver启动和处理http流程分析 的全部内容, 来源链接: utcz.com/z/517810.html