亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Django 源碼WSGI剖析過程詳解

發布時間:2020-10-16 14:33:10 來源:腳本之家 閱讀:160 作者:搗亂小子 欄目:開發技術

前言

python 作為一種腳本語言, 已經逐漸大量用于 web 后臺開發中, 而基于 python 的 web 應用程序框架也越來越多, Bottle, Django, Flask 等等.

在一個 HTTP 請求到達服務器時, 服務器接收并調用 web 應用程序解析請求, 產生響應數據并返回給服務器. 這里涉及了兩個方面的東西: 服務器(server)和應用程序(application). 勢必要有一個合約要求服務器和應用程序都去遵守, 如此按照此合約開發的無論是服務器還是應用程序都會具有較大的普遍性. 而這就好像在計算機通信的早期, 各大公司都有屬于自己的通信協議, 如此只會讓市場雜亂無章, 寧愿只要一種通信協議.

而針對 python 的合約是 WSGI(Python Web Server Gateway Interface). 具體的規定見 PEP 333.

實習的時候一直使用 Django, 下面是結合 Django 學習 WSGI 的筆記.

application/應用程序

在應用程序一方面, 必須提供下面的方法:

def simple_app(environ, start_response):
  """可能是最簡單的處理了"""
  status = '200 OK'
  response_headers = [('Content-type', 'text/plain')]
  start_response(status, response_headers)
  return ['Hello world!\n'] # 返回結果必須可迭代

除了方法以外, 還可以用實現了 __call__ 的類實現.

它會被服務器調用, 在這里 environ 是一個字典, 包含了環境變量, REQUEST_METHOD,SCRIPT_NAME,QUERY_STRING 等; start_response 是一個回調函數, 會在 simple_app 中被調用, 主要用來開始響應 HTTP. start_response 原型大概是這樣:

def start_response(status, response_headers, exc_info=None):
  ...
  return write # 返回這 write 函數 只是為了兼容之前的 web 框架, 新的框架根本用不到.

參數有 status 即狀態碼; response_headers HTTP 頭, 可以修改; exc_info 是與錯誤相關的信息, 在產生相應數據過程中可能發生錯誤, 這時需要更新 HTTP 頭部, 通過再次調用 start_response 可以實現. 因此更為詳盡的實現寫法可能是這種:

def start_response(status, response_headers, exc_info=None):
  if exc_info:
     try:
       # do stuff w/exc_info here
     finally:
       exc_info = None  # Avoid circular ref.
  return write

Server/服務器

在服務器方面, 可以想象最簡單的工作就是調用 simple_app(), 然后向客戶端發送數據:

result = simple_app(environ, start_response) #名字不一定為 simple_app
try:
  for data in result:
    if data:  # don't send headers until body appears
      write(data)
  if not headers_sent:
    write('')  # send headers now if body was empty
finally:
  if hasattr(result, 'close'):
    result.close()

注意 WSGI 并沒有事無巨細規定 web 應用程序和服務器內部的工作方式, 只是是規定了它們之間連接的標準.

python wsgiref 模塊

下面看看 Django 是如何實現 WSGI 的. Django 其內部已經自帶了一個方便本地測試的小服務器, 所以在剛開始學習 Django 的時候并不需搭建 apache 或者 nginx 服務器. Django 自帶的服務器基于 python wsgiref 模塊實現, 它自帶的測試代碼:

# demo_app() 是 application
def demo_app(environ,start_response):
  from StringIO import StringIO
  stdout = StringIO()
  print >>stdout, "Hello world!"
  print >>stdout
  h = environ.items(); h.sort()
  for k,v in h:
    print >>stdout, k,'=', repr(v)
  start_response("200 OK", [('Content-Type','text/plain')])
  return [stdout.getvalue()]

def make_server(
  host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler
):
  """Create a new WSGI server listening on `host` and `port` for `app`"""
  server = server_class((host, port), handler_class)
  server.set_app(app)
  return server

if __name__ == '__main__':
  httpd = make_server('', 8000, demo_app)
  sa = httpd.socket.getsockname()
  print "Serving HTTP on", sa[0], "port", sa[1], "..."
  import webbrowser
  webbrowser.open('http://localhost:8000/xyz?abc')
  httpd.handle_request() # serve one request, then exit

python 的庫有好多的工具, 這時可能因為需要的原因, 會生出好多的父類, 為了講明, 根據 wsgiref 模塊和它自帶的測試用例得出下面的 UML 圖(注意, 這只是 wsgiref, 沒有涉及 Django):

Django 源碼WSGI剖析過程詳解

我讀完這些的時候已經暈了, 確實是里邊的繼承關系有些復雜. 因此, 簡要的概括了測試代碼的執行關系:

  • 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() 關閉請求(其實什么也沒做)

ps: 明明 application 是 WSGIServer 的屬性, 為什么會在 ServerHandler 中調用? 因為在實例化 WSGIRequestHandler 的時候 WSGIServer 把自己搭進去了, 所以在 WSGIRequestHandler 中實例化 ServerHandler 時候可以通過 WSGIRequestHandler.server.get_app() 得到真正的 application.

總結

從上面可以得到, 啟動服務器的時候, 無論以什么方式都要給它傳遞一個 application(), 是一個函數也好, 一個實現了 __call__ 的類也好; 當請求到達服務器的時候, 服務器自會調用 application(), 從而得到相應數據. 至于, 對請求的數據如何相應, application() 中可以細化.

確實, 其中的調用鏈太過長, 這期間還沒有加入 HTTP 頭的分析(提取 Cookie等). 如果只為響應一個 "helloworld", 在 WSGIServer.finish_request() 中直接相應數據就好了, WSGIRequestHandler 和 ServerHandler 類可以直接省去, 而只需要你提供一個 application()! 但事實上, 并不只是相應 "helloworld" 那樣簡單...

關于 Django 中的 WSGI 如何, 下一節再說. Django 源碼剖析從這里開始! 我已經在 github 備份了 Django 源碼的注釋: Decode-Django, 有興趣的童鞋 fork 吧. 本文結合 python wsgiref, BaseHTTPServer.py, SocketServer.py 模塊源碼看更好.

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

合作市| 探索| 县级市| 息烽县| 曲水县| 崇阳县| 新安县| 板桥市| 奉化市| 承德县| 醴陵市| 庆城县| 托克逊县| 会宁县| 四平市| 新野县| 长武县| 汾阳市| 托克托县| 万载县| 新化县| 通许县| 红安县| 砀山县| 海伦市| 武威市| 神农架林区| 鄄城县| 商丘市| 西乌珠穆沁旗| 南靖县| 兰西县| 涪陵区| 大宁县| 璧山县| 南开区| 曲松县| 彭水| 启东市| 叙永县| 万盛区|