Django 响应数据response的返回源码详解


Posted in Python onAugust 06, 2019

响应数据的返回

在 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 吧.

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python itchat实现微信自动回复的示例代码
Aug 14 Python
python机器学习理论与实战(四)逻辑回归
Jan 19 Python
kaggle+mnist实现手写字体识别
Jul 26 Python
python控制nao机器人身体动作实例详解
Apr 29 Python
Django高级编程之自定义Field实现多语言
Jul 02 Python
Python爬取智联招聘数据分析师岗位相关信息的方法
Aug 13 Python
Python timer定时器两种常用方法解析
Jan 20 Python
详解Python中的分支和循环结构
Feb 11 Python
Python requests.post方法中data与json参数区别详解
Apr 30 Python
python中用ctypes模拟点击的实例讲解
Nov 26 Python
如何用用Python将地址标记在地图上
Feb 07 Python
python opencv检测直线 cv2.HoughLinesP的实现
Jun 18 Python
详解Python Matplotlib解决绘图X轴值不按数组排序问题
Aug 05 #Python
Django中提供的6种缓存方式详解
Aug 05 #Python
python修改字典键(key)的方法
Aug 05 #Python
python中使用while循环的实例
Aug 05 #Python
Python3 列表,数组,矩阵的相互转换的方法示例
Aug 05 #Python
Python中print函数简单使用总结
Aug 05 #Python
Numpy数组array和矩阵matrix转换方法
Aug 05 #Python
You might like
无法在发生错误时创建会话,请检查 PHP 或网站服务器日志,并正确配置 PHP 安装最快的解决办法
2010/08/01 PHP
解析关于java,php以及html的所有文件编码与乱码的处理方法汇总
2013/06/24 PHP
ThinkPHP3.2.2的插件控制器功能
2015/03/05 PHP
phpcms中的评论样式修改方法
2016/10/21 PHP
PHP中功能强大却很少使用的函数实例小结
2016/11/10 PHP
php设计模式之原型模式分析【星际争霸游戏案例】
2020/03/23 PHP
解放web程序员的输入验证
2006/10/06 Javascript
jquery实现表格奇数偶数行不同样式(有图为证及实现代码)
2013/01/23 Javascript
jquery获取tr中控件值并操作tr实现思路
2013/03/27 Javascript
js实现点小图看大图效果的思路及示例代码
2013/10/28 Javascript
JS动态添加与删除select中的Option对象(示例代码)
2013/12/20 Javascript
用svg制作富有动态的tooltip
2015/07/17 Javascript
Jquery组件easyUi实现选项卡切换示例
2016/08/23 Javascript
手机端 HTML5使用photoswipe.js仿微信朋友圈图片放大效果
2016/08/25 Javascript
概述一个页面从输入URL到页面加载完的过程
2016/12/16 Javascript
Angular4的输入属性与输出属性实例详解
2017/11/29 Javascript
彻底理解js面向对象之继承
2018/02/04 Javascript
vue的toast弹窗组件实例详解
2018/05/14 Javascript
在vue中阻止浏览器后退的实例
2019/11/06 Javascript
Vue 3.0 全家桶抢先体验
2020/04/28 Javascript
[02:20]DOTA2亚洲邀请赛 IG战队出场宣传片
2015/02/07 DOTA
python计算圆周长、面积、球体体积并画出圆
2014/04/08 Python
Python获取系统所有进程PID及进程名称的方法示例
2018/05/24 Python
在Django中URL正则表达式匹配的方法
2018/12/20 Python
关于Python 常用获取元素 Driver 总结
2019/11/24 Python
Python之变量类型和if判断方式
2020/05/05 Python
详解pyqt5的UI中嵌入matplotlib图形并实时刷新(挖坑和填坑)
2020/08/07 Python
css3媒体查询中device-width和width的区别详解
2020/03/27 HTML / CSS
中医专业应届生求职信
2013/11/17 职场文书
餐厅总经理岗位职责
2013/12/31 职场文书
汽车维修求职信
2014/06/15 职场文书
2015年党日活动总结范文
2015/03/25 职场文书
试用期转正工作总结2015
2015/05/28 职场文书
新课程改革心得体会
2016/01/22 职场文书
pytorch加载预训练模型与自己模型不匹配的解决方案
2021/05/13 Python
详解vue中v-for的key唯一性
2021/05/15 Vue.js