响应数据的返回

在 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() 返回数据」开始说起, 下面是主要的代码解说:

# 下面的函数都在 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

http://daoluan.net

Django 源码小剖: 响应数据 response 的返回的更多相关文章

  1. Django 源码小剖: 初探 WSGI

    Django 源码小剖: 初探 WSGI python 作为一种脚本语言, 已经逐渐大量用于 web 后台开发中, 而基于 python 的 web 应用程序框架也越来越多, Bottle, Djan ...

  2. Django 源码小剖: 应用程序入口 WSGIHandler

    WSGI 有三个部分, 分别为服务器(server), 应用程序(application) 和中间件(middleware). 已经知道, 服务器方面会调用应用程序来处理请求, 在应用程序中有真正的处 ...

  3. Django 源码小剖: Django 对象关系映射(ORM)

    引 从前面已经知道, 一个 request 的到来和一个对应 response 的返回的流程, 数据处理和数据库离不开. 我们也经常在 views.py 的函数定义中与数据库打交道. django O ...

  4. Django 源码小剖: 更高效的 URL 调度器(URL dispatcher)

    效率问题 django 内部的 url 调度机制说白了就是给一张有关匹配信息的表, 这张表中有着 url -> action 的映射, 当请求到来的时候, 一个一个(遍历)去匹配. 中, 则调用 ...

  5. Django 源码小剖: URL 调度器(URL dispatcher)

    在刚开始接触 django 的时候, 我们尝试着从各种入门文档中创建一个自己的 django 项目, 需要在 mysite.urls.py 中配置 URL. 这是 django url 匹配处理机制的 ...

  6. Django 源码小剖: 初探中间件(middleware)

    因为考虑到文章的长度, 所以 BaseHandler 的展开被推迟了. 在 BaseHandler 中隐藏着中间件的信息, 较常见的 SessionMiddleware 就已经默认安装.  BaseH ...

  7. Django 源码小剖: Django ORM 查询管理器

    ORM 查询管理器 对于 ORM 定义: 对象关系映射, Object Relational Mapping, ORM, 是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换.从 ...

  8. Django 源码小剖: Django 中的 WSGI

    Django 其内部已经自带了一个方便本地测试的小服务器, 所以在刚开始学习 Django 的时候并不需搭建 apache 或者 nginx 服务器. Django 自带的服务器基于 python w ...

  9. urllib2 源码小剖

    urllib2 源码小剖 2013-08-25 23:38 by 捣乱小子, 272 阅读, 0 评论, 收藏, 编辑 两篇小剖已经完成: urllib 源码小剖 urllib2 源码小剖 urlli ...

随机推荐

  1. ubuntu 命令的快捷启动

    目前我知道的有三种方式 第一种,在配置文件 .bashrc 中 配置alias ,追加到文件末尾就行 如下: alias tmout='tail -f /usr/local/oakcloud/tomc ...

  2. javascript基础知识-语句

    关于javascript语句,有下面一些有趣的用法. 1.空语句的使用: 空语句只包含一个";",那在什么时候可以使用呢? 例: //初始化一个数组a for(i = 0;i &l ...

  3. android listview用adapter.notifyDataSetChanged()无法刷新每项的图标

    http://blog.csdn.net/caizhegnhao/article/details/41318575 今天在开发中遇到一个很奇怪的listview的问题. 这个问题情景是我的应用需要做一 ...

  4. JAVA学习博客---2015.5

    上一次的学习博客写的和流水账差不多,有点生硬的和背目录一样,所以既然学习的目的是程序,那么这个月的学习博客就用程序来说点东西吧.这个月看了一些C和JAVA的视频,开始看别人写的程序,能看的懂但是自己去 ...

  5. ASP.NET Core 1.0 中使用 Swagger 生成文档

    github:https://github.com/domaindrivendev/Ahoy 之前文章有介绍在ASP.NET WebAPI 中使用Swagger生成文档,ASP.NET Core 1. ...

  6. 设计模式之美:Flyweight(享元)

    索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):使用 FlyweightFactory 管理 Flyweight 对象. 意图 运用共享技术有效地支持大量细粒度的对象. Use ...

  7. LoadTest内存和线程Troubleshooting实战

    在端午节放假的三天中,我对正在开发的 Service 进行了 LoadTest,尝试在增大压力的条件下发现问题. 该 Service 为独立进程的 WCF 服务,宿主于 WindowsService, ...

  8. 用 NuGet Package Explorer 管理你的攻城武器

    缘由:每次新建一个工程,总是要从自己的“弹药库”或者之前的工程里面手动引用一些类库和脚本插件,难免有些繁琐和遗漏.想起经常用到的NuGet,跑到NuGet主页一看,发现有 NuGet Package ...

  9. [算法] [常微分方程] [欧拉法 改进欧拉法 经典R-K算法]

    #include<iostream> #include<cmath> #include<cstdio> #include<iomanip> using ...

  10. php性能分析工具 - xhprof的安装使用

    一.前言 有用的东西还是记录下来吧,也方便以后的查询:这次记录一下xhprof的安装使用: xhprof是facebook开源出来的一个php轻量级的性能分析工具,跟Xdebug类似,但性能开销更低, ...