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

回到顶部