Django 響應(yīng)數(shù)據(jù)response的返回源碼詳解
響應(yīng)數(shù)據(jù)的返回
在 WSGIHandler.__call__(self, environ, start_response) 方法調(diào)用了 WSGIHandler.get_response() 方法, 由此得到響應(yīng)數(shù)據(jù)對(duì)象 response. 如今所要做的, 便是將其返回給客戶端. 在 Django 源碼小剖: 初探 WSGI 中, 簡(jiǎn)要的概括了請(qǐng)求到來(lái)時(shí) django 自帶服務(wù)器的執(zhí)行關(guān)系, 摘抄如下:
- make_server() 中 WSGIServer 類已經(jīng)作為服務(wù)器類, 負(fù)責(zé)接收請(qǐng)求, 調(diào)用 application 的處理, 返回相應(yīng);
- WSGIRequestHandler 作為請(qǐng)求處理類, 并已經(jīng)配置在 WSGIServer 中;
- 接著還設(shè)置了 WSGIServer.application 屬性(set_app(app));
- 返回 server 實(shí)例.
- 接著打開(kāi)瀏覽器, 即發(fā)起請(qǐng)求. 服務(wù)器實(shí)例 WSGIServer httpd 調(diào)用自身 handle_request() 函數(shù)處理請(qǐng)求. handle_request() 的工作流程如下:請(qǐng)求-->WSGIServer 收到-->調(diào)用 WSGIServer.handle_request()-->調(diào)用 _handle_request_noblock()-->調(diào)用 process_request()-->調(diào)用 finish_request()-->finish_request() 中實(shí)例化 WSGIRequestHandler-->實(shí)例化過(guò)程中會(huì)調(diào)用 handle()-->handle() 中實(shí)例化 ServerHandler-->調(diào)用 ServerHandler.run()-->run() 調(diào)用 application() 這才是真正的邏輯.-->run() 中在調(diào)用 ServerHandler.finish_response() 返回?cái)?shù)據(jù)-->回到 process_request() 中調(diào)用 WSGIServer.shutdown_request() 關(guān)閉請(qǐng)求(其實(shí)什么也沒(méi)做)
事實(shí)上, WSGIServer 并沒(méi)有負(fù)責(zé)將響應(yīng)數(shù)據(jù)返回給客戶端, 它將客戶端的信息(如最重要的客戶端 socket 套接字)交接給了 WSGIRequestHandler, WSGIRequestHandler 又將客戶端的信息交接給了 ServerHandler, 所以 ServerHandler 產(chǎn)生響應(yīng)數(shù)據(jù)對(duì)象后, 會(huì)直接返回給客戶端.
代碼剖析
從「調(diào)用 ServerHandler.run()-->run() 調(diào)用 application() 這才是真正的邏輯.-->run() 中在調(diào)用 ServerHandler.finish_response() 返回?cái)?shù)據(jù)」開(kāi)始說(shuō)起, 下面是主要的代碼解說(shuō):
# 下面的函數(shù)都在 ServerHandler 的繼承鏈上方法, 有些方法父類只定義了空方法, 具體邏輯交由子類實(shí)現(xiàn). 有關(guān)繼承鏈請(qǐng)參看: http://daoluan.net/blog/decode-django-wsgi/
def run(self, application):
"""Invoke the application"""
try:
self.setup_environ()
# application 在 django 中就是 WSGIHandler 類, 他實(shí)現(xiàn)了 __call__ 方法, 所以行為和函數(shù)一樣.
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:
# 向套接字寫(xiě)數(shù)據(jù), 將數(shù)據(jù)返回給客戶端
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()")
# 需要先發(fā)送 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()
# 再發(fā)送實(shí)體
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"
# 必須先調(diào)用 self.start_response() 設(shè)置狀態(tài)碼
if not self.status:
raise AssertionError("write() before start_response()")
# 需要先發(fā)送 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()
# 再發(fā)送實(shí)體
else:
self.bytes_sent += len(data)
# XXX check Content-Length and truncate if too many bytes written? 是否需要分段發(fā)送過(guò)大的數(shù)據(jù)?
# If data is too large, socket will choke, 窒息死掉 so write chunks no larger
# than 32MB at a time.
# 分片發(fā)送
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):
# 如果是第一次調(diào)用, 則調(diào)用 stdout.write(), 理解為一個(gè)套接字對(duì)象
self.stdout.write(data)
# 第二次調(diào)用就是直接調(diào)用 stdout.write() 了
self._write = self.stdout.write
接下來(lái)的事情, 就是回到 WSGIServer 關(guān)閉套接字, 清理現(xiàn)場(chǎng), web 應(yīng)用程序由此結(jié)束; 但服務(wù)器依舊在監(jiān)聽(tīng)(WSGIServer 用 select 實(shí)現(xiàn))是否有新的請(qǐng)求, 不展開(kāi)了.
階段性的總結(jié)
請(qǐng)求到來(lái)至數(shù)據(jù)相應(yīng)的流程已經(jīng)走了一遍, 包括 django 內(nèi)部服務(wù)器是如何運(yùn)作的, 請(qǐng)求到來(lái)是如何工作的, 響應(yīng)數(shù)據(jù)對(duì)象是如何產(chǎn)生的, url 是如何調(diào)度的, views.py 中定義的方法是何時(shí)調(diào)用的, 響應(yīng)數(shù)據(jù)是如何返回的...另外還提出了一個(gè)更好的 url 調(diào)度策略, 如果你有更好的方法, 不忘與大家分享.
我已經(jīng)在 github 備份了 Django 源碼的注釋: Decode-Django, 有興趣的童鞋 fork 吧.
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Python?range函數(shù)生成一系列連續(xù)整數(shù)的內(nèi)部機(jī)制解析
這篇文章主要為大家介紹了Python?range函數(shù)生成一系列連續(xù)整數(shù)的內(nèi)部機(jī)制解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
Python實(shí)現(xiàn)定時(shí)自動(dòng)關(guān)閉的tkinter窗口方法
今天小編就為大家分享一篇Python實(shí)現(xiàn)定時(shí)自動(dòng)關(guān)閉的tkinter窗口方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-02-02
python實(shí)現(xiàn)定制交互式命令行的方法
這篇文章主要介紹了python實(shí)現(xiàn)定制交互式命令行的方法,需要的朋友可以參考下2014-07-07
python監(jiān)控網(wǎng)站運(yùn)行異常并發(fā)送郵件的方法
這篇文章主要介紹了python監(jiān)控網(wǎng)站運(yùn)行異常并發(fā)送郵件的方法,涉及Python操作郵件及服務(wù)器監(jiān)控的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03
Python面向?qū)ο筮M(jìn)階學(xué)習(xí)
在本文里我們整理了關(guān)于Python面向?qū)ο蟮倪M(jìn)階學(xué)習(xí)知識(shí)點(diǎn)以及學(xué)習(xí)路線等內(nèi)容,有興趣的朋友們學(xué)習(xí)下。2019-05-05
對(duì)python 生成拼接xml報(bào)文的示例詳解
今天小編就為大家分享一篇對(duì)python 生成拼接xml報(bào)文的示例詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
一文教你學(xué)會(huì)使用Python中的多處理模塊
Python?多處理模塊是一個(gè)強(qiáng)大的工具,用于實(shí)現(xiàn)并行處理,提高程序的性能和效率,本文將詳細(xì)介紹?Python?中多處理模塊的使用方法,希望對(duì)大家有所幫助2024-01-01
Pythonr基于selenium如何實(shí)現(xiàn)不同商城的商品價(jià)格差異分析系統(tǒng)
這篇文章主要給大家介紹了關(guān)于Pythonr基于selenium如何實(shí)現(xiàn)不同商城的商品價(jià)格差異分析系統(tǒng)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-03-03

