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同时兼容python2和python3的8个技巧分享
Jul 11 Python
Python中生成器和yield语句的用法详解
Apr 17 Python
Linux RedHat下安装Python2.7开发环境
May 20 Python
python中的字符串内部换行方法
Jul 19 Python
pygame游戏之旅 添加游戏暂停功能
Nov 21 Python
对python使用telnet实现弱密码登录的方法详解
Jan 26 Python
numpy.where() 用法详解
May 27 Python
Python搭建代理IP池实现获取IP的方法
Oct 27 Python
Python3 集合set入门基础
Feb 10 Python
python获得命令行输入的参数的两种方式
Nov 02 Python
浅谈Selenium 控制浏览器的常用方法
Dec 04 Python
Python标准库pathlib操作目录和文件
Nov 20 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
setcookie中Cannot modify header information-headers already sent by错误的解决方法详解
2013/05/08 PHP
php的socket编程详解
2016/11/20 PHP
PHP中Notice错误常见解决方法
2017/04/28 PHP
YII2框架中excel表格导出的方法详解
2017/07/21 PHP
js实现倒计时(距离结束还有)示例代码
2013/07/24 Javascript
JavaScript判断变量是对象还是数组的方法
2014/08/28 Javascript
js使用for循环与innerHTML获取选中tr下td值
2014/09/26 Javascript
基于javascript实现判断移动终端浏览器版本信息
2014/12/09 Javascript
简单介绍JavaScript数据类型之隐式类型转换
2015/12/28 Javascript
BootStrap入门教程(三)之响应式原理
2016/09/19 Javascript
Vue组件开发初探
2017/02/14 Javascript
Javascript中的async awai的用法
2017/05/17 Javascript
nodejs中解决异步嵌套循环和循环嵌套异步的问题
2017/07/12 NodeJs
bootstrap-table实现服务器分页的示例 (spring 后台)
2017/09/01 Javascript
9种改善AngularJS性能的方法
2017/11/28 Javascript
了解ESlint和其相关操作小结
2018/05/21 Javascript
微信小程序实现树莓派(raspberry pi)小车控制
2020/02/12 Javascript
vue倒计时刷新页面不会从头开始的解决方法
2020/03/03 Javascript
wxpython 学习笔记 第一天
2009/02/09 Python
Python解释执行原理分析
2014/08/22 Python
使用Python设置tmpfs来加速项目的教程
2015/04/17 Python
Python中Continue语句的用法的举例详解
2015/05/14 Python
python实现数值积分的Simpson方法实例分析
2015/06/05 Python
PyTorch线性回归和逻辑回归实战示例
2018/05/22 Python
Python使用pyautogui模块实现自动化鼠标和键盘操作示例
2018/09/04 Python
pytorch 共享参数的示例
2019/08/17 Python
如何基于python实现脚本加密
2019/12/28 Python
Python内置类型性能分析过程实例
2020/01/29 Python
利用Python过滤相似文本的简单方法示例
2021/02/03 Python
你可能不熟练的十个前端HTML5经典面试题
2018/07/03 HTML / CSS
Stefania Mode英国:奢华设计师和时尚服装
2017/10/23 全球购物
标准大学生职业生涯规划书写作指南
2014/09/18 职场文书
淘宝客服专员岗位职责
2015/04/07 职场文书
超市督导岗位职责
2015/04/10 职场文书
python使用matplotlib绘制图片时x轴的刻度处理
2021/08/30 Python
「偶像大师 MILLION LIVE!」七尾百合子手办开订
2022/03/21 日漫