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 相关文章推荐
pyside写ui界面入门示例
Jan 22 Python
用Python的Flask框架结合MySQL写一个内存监控程序
Nov 07 Python
Python 正则表达式的高级用法
Dec 04 Python
python中numpy基础学习及进行数组和矢量计算
Feb 12 Python
python3+PyQt5重新实现QT事件处理程序
Apr 19 Python
用python处理图片之打开\显示\保存图像的方法
May 04 Python
Python编写合并字典并实现敏感目录的小脚本
Feb 26 Python
Python 生成器,迭代,yield关键字,send()传参给yield语句操作示例
Oct 12 Python
Python csv文件的读写操作实例详解
Nov 19 Python
python各种excel写入方式的速度对比
Nov 10 Python
Python虚拟环境virtualenv创建及使用过程图解
Dec 08 Python
Python使用socket去实现TCP客户端和TCP服务端
Apr 12 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中Object对象的笔记分享
2011/06/28 PHP
TMDPHP 模板引擎使用教程
2012/03/13 PHP
修改apache配置文件去除thinkphp url中的index.php
2014/01/17 PHP
浅谈PHP解析URL函数parse_url和parse_str
2014/11/11 PHP
php将数组存储为文本文件方法汇总
2015/10/28 PHP
PHP实现的猴王算法(猴子选大王)示例
2018/04/30 PHP
PHP中关于php.ini参数优化详解
2020/02/28 PHP
基于jQuery的淡入淡出可自动切换的幻灯插件
2010/08/24 Javascript
js实现弹窗插件功能实例代码分享
2013/12/12 Javascript
js实现感应鼠标图片透明度变化的方法
2015/02/20 Javascript
关于js里的this关键字的理解
2015/08/17 Javascript
JavaScript中有关一个数组中最大值和最小值及它们的下表的输出的解决办法
2016/07/01 Javascript
jquery插件bootstrapValidator表单验证详解
2016/12/15 Javascript
深入解析js轮播插件核心代码的实现过程
2017/04/14 Javascript
fetch 如何实现请求数据
2018/12/20 Javascript
微信小程序实现商品属性联动选择
2019/02/15 Javascript
webpack4 从零学习常用配置(小结)
2019/05/28 Javascript
vue接通后端api以及部署到服务器操作
2020/08/13 Javascript
[03:18]DOTA2亚洲邀请赛小组赛第一日 RECAP赛事回顾
2015/01/30 DOTA
Flask SQLAlchemy一对一,一对多的使用方法实践
2013/02/10 Python
一则python3的简单爬虫代码
2014/05/26 Python
基于DataFrame改变列类型的方法
2018/07/25 Python
Python Web框架之Django框架文件上传功能详解
2019/08/16 Python
pyinstaller 3.6版本通过pip安装失败的解决办法(推荐)
2020/01/18 Python
Python基于内置库pytesseract实现图片验证码识别功能
2020/02/24 Python
Boden美国官网:英伦原创时装品牌
2017/07/03 全球购物
斯洛伐克电子产品购物网站:DATART
2020/04/05 全球购物
软件测试英文面试题
2012/10/14 面试题
写求职信有什么意义
2014/02/17 职场文书
法律进企业活动方案
2014/03/04 职场文书
食品安全汇报材料
2014/08/18 职场文书
2014镇副书记群众路线专题民主生活会思想汇报
2014/09/23 职场文书
单位实习介绍信
2015/05/05 职场文书
西游降魔篇观后感
2015/06/15 职场文书
PHP中->和=>的意思
2021/03/31 PHP
Shell脚本一键安装Nginx服务自定义Nginx版本
2022/03/20 Servers