Django 源码小剖: 响应数据 response 的返回
2013-09-23 23:30 捣乱小子 阅读(3483) 评论(1) 编辑 收藏 举报响应数据的返回
在 WSGIHandler.__call__(self, environ, start_response) 方法调用了 WSGIHandler.get_response() 方法, 由此得到响应数据对象 response. 如今所要做的, 便是将其返回给客户端. 在 Django 源码小剖: 初探 WSGI 中, 简要的概括了请求到来时 django 自带服务器的执行关系, 摘抄如下:
- make_server() 中 WSGIServer 类已经作为服务器类, 负责接收请求, 调用 application 的处理, 返回相应;
- WSGIRequestHandler 作为请求处理类, 并已经配置在 WSGIServer 中;
- 接着还设置了 WSGIServer.application 属性(set_app(app));
- 返回 server 实例.
- 接着打开浏览器, 即发起请求. 服务器实例 WSGIServer httpd 调用自身 handle_request() 函数处理请求. handle_request() 的工作流程如下:请求-->WSGIServer 收到-->调用 WSGIServer.handle_request()-->调用 _handle_request_noblock()-->调用 process_request()-->调用 finish_request()-->finish_request() 中实例化 WSGIRequestHandler-->实例化过程中会调用 handle()-->handle() 中实例化 ServerHandler-->调用 ServerHandler.run()-->run() 调用 application() 这才是真正的逻辑.-->run() 中在调用 ServerHandler.finish_response() 返回数据-->回到 process_request() 中调用 WSGIServer.shutdown_request() 关闭请求(其实什么也没做)
事实上, WSGIServer 并没有负责将响应数据返回给客户端, 它将客户端的信息(如最重要的客户端 socket 套接字)交接给了 WSGIRequestHandler, WSGIRequestHandler 又将客户端的信息交接给了 ServerHandler, 所以 ServerHandler 产生响应数据对象后, 会直接返回给客户端.
代码剖析
从「调用 ServerHandler.run()-->run() 调用 application() 这才是真正的逻辑.-->run() 中在调用 ServerHandler.finish_response() 返回数据」开始说起, 下面是主要的代码解说:
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | # 下面的函数都在 ServerHandler 的继承链上方法, 有些方法父类只定义了空方法, 具体逻辑交由子类实现. 有关继承链请参看: http://daoluan.net/blog/decode-django-wsgi/ def run( self , application): """Invoke the application""" try : self .setup_environ() # application 在 django 中就是 WSGIHandler 类, 他实现了 __call__ 方法, 所以行为和函数一样. self .result = application( self .environ, self .start_response) self .finish_response() except : # handle error 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() def write( self , data): """'write()' callable as specified by PEP 333""" # 必须是都是字符 assert type (data) is StringType, "write() argument must be string" if not self .status: raise AssertionError( "write() before start_response()" ) # 需要先发送 HTTP 头 elif not self .headers_sent: # Before the first output, send the stored headers self .bytes_sent = len (data) # make sure we know content-length self .send_headers() # 再发送实体 else : self .bytes_sent + = len (data) # XXX check Content-Length and truncate if too many bytes written? self ._write(data) self ._flush() def write( self , data): """'write()' callable as specified by PEP 3333""" assert isinstance (data, bytes), "write() argument must be bytestring" # 必须先调用 self.start_response() 设置状态码 if not self .status: raise AssertionError( "write() before start_response()" ) # 需要先发送 HTTP 头 elif not self .headers_sent: # Before the first output, send the stored headers self .bytes_sent = len (data) # make sure we know content-length self .send_headers() # 再发送实体 else : self .bytes_sent + = len (data) # XXX check Content-Length and truncate if too many bytes written? 是否需要分段发送过大的数据? # If data is too large, socket will choke, 窒息死掉 so write chunks no larger # than 32MB at a time. # 分片发送 length = len (data) if length > 33554432 : offset = 0 while offset < length: chunk_size = min ( 33554432 , length) self ._write(data[offset:offset + chunk_size]) self ._flush() offset + = chunk_size else : self ._write(data) self ._flush() def _write( self ,data): # 如果是第一次调用, 则调用 stdout.write(), 理解为一个套接字对象 self .stdout.write(data) # 第二次调用就是直接调用 stdout.write() 了 self ._write = self .stdout.write |
接下来的事情, 就是回到 WSGIServer 关闭套接字, 清理现场, web 应用程序由此结束; 但服务器依旧在监听(WSGIServer 用 select 实现)是否有新的请求, 不展开了.
阶段性的总结
请求到来至数据相应的流程已经走了一遍, 包括 django 内部服务器是如何运作的, 请求到来是如何工作的, 响应数据对象是如何产生的, url 是如何调度的, views.py 中定义的方法是何时调用的, 响应数据是如何返回的...另外还提出了一个更好的 url 调度策略, 如果你有更好的方法, 不忘与大家分享.
我已经在 github 备份了 Django 源码的注释: Decode-Django, 有兴趣的童鞋 fork 吧.
捣乱 2013-9-23
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步