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的类实例属性访问规则探讨
Jan 30 Python
Python中列表、字典、元组数据结构的简单学习笔记
Mar 20 Python
基于Python 的进程管理工具supervisor使用指南
Sep 18 Python
Python3.4 splinter(模拟填写表单)使用方法
Oct 13 Python
使用python绘制二元函数图像的实例
Feb 12 Python
Python中调用其他程序的方式详解
Aug 06 Python
django drf框架自带的路由及最简化的视图
Sep 10 Python
Python+opencv+pyaudio实现带声音屏幕录制
Dec 23 Python
详解python 降级到3.6终极解决方案
Feb 06 Python
keras自动编码器实现系列之卷积自动编码器操作
Jul 03 Python
python 基于opencv实现图像增强
Dec 23 Python
Pytorch 统计模型参数量的操作 param.numel()
May 13 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
Search File Contents PHP 搜索目录文本内容的代码
2010/02/21 PHP
PHP中通过语义URL防止网站被攻击的方法分享
2011/09/08 PHP
ThinkPHP3.2.3数据库设置新特性
2015/03/05 PHP
php计算两个坐标(经度,纬度)之间距离的方法
2015/04/17 PHP
ThinkPHP控制器详解
2015/07/27 PHP
PHP安全之register_globals的on和off的区别
2020/07/23 PHP
javascript在一段文字中的光标处插入其他文字
2007/08/26 Javascript
jQuery EasyUI API 中文文档 - ComboGrid 组合表格
2011/10/13 Javascript
Jquery $.getJSON 在IE下的缓存问题解决方法
2014/10/10 Javascript
javascript中sort() 方法使用详解
2015/08/30 Javascript
ECMA5数组的新增方法有哪些及forEach()模仿实现
2015/11/03 Javascript
跟我学习javascript的最新标准ES6
2015/11/20 Javascript
JS实现鼠标移上去显示图片或微信二维码
2016/12/14 Javascript
Vue报错:Uncaught TypeError: Cannot assign to read only property’exports‘ of object’#‘的解决方法
2017/06/17 Javascript
BootStrap模态框和select2合用时input无法获取焦点的解决方法
2017/09/01 Javascript
将jquery.qqFace.js表情转换成微信的字符码
2017/12/01 jQuery
微信小程序日期选择器实例代码
2018/07/18 Javascript
对vuejs的v-for遍历、v-bind动态改变值、v-if进行判断的实例讲解
2018/08/27 Javascript
对vue v-if v-else-if v-else 的简单使用详解
2018/09/29 Javascript
JS数组求和的常用方法实例小结
2019/01/07 Javascript
Angular6 用户自定义标签开发的实现方法
2019/01/08 Javascript
vue项目中定义全局变量、函数的几种方法
2019/11/08 Javascript
JavaScript实现简单的计算器
2020/01/16 Javascript
jquery.validate自定义验证用法实例分析【成功提示与择要提示】
2020/06/06 jQuery
Django的数据模型访问多对多键值的方法
2015/07/21 Python
Python中numpy模块常见用法demo实例小结
2019/03/16 Python
python误差棒图errorbar()函数实例解析
2020/02/11 Python
Scrapy-Redis之RedisSpider与RedisCrawlSpider详解
2020/11/18 Python
悬挂训练绳:TRX
2017/12/14 全球购物
优秀志愿者事迹材料
2014/02/03 职场文书
物业管理专业自荐信
2014/07/01 职场文书
祖国在我心中演讲稿600字
2014/09/23 职场文书
2014年电话客服工作总结
2014/12/09 职场文书
MySql 8.0及对应驱动包匹配的注意点说明
2021/06/23 MySQL
CPU不支持Windows11系统怎么办
2021/11/21 数码科技
SQL解决未能删除约束问题drop constraint
2022/05/30 SQL Server