Wsgiref 包——符合 WSGI 標準的 Web 服務實現(一)

發表於2017-09-23

引子

在前幾篇文章:SocketServer——網路通訊伺服器BaseHTTPServer——實現Web伺服器SimpleHTTPServer——一個簡單的HTTP伺服器,我們介紹了Python標準庫對於網路通訊的支援,並且還介紹了標準庫中的一些模組,例如TCPServerUDPServerBaseHTTPServer等。這些模組能夠實現基礎的網路通訊服務,例如TCP/UDP層的通訊、HTTP應用層的通訊。

上述模組對於網路通訊的實現,基本的流程是:

  • 建立一個伺服器。例如TCP伺服器、UDP伺服器、HTTP伺服器。伺服器可以監聽和接收請求;
  • 建立請求處理程式。請求處理程式可以解析到達的請求,併發回一個響應。

以上流程基本上反映了Python標準庫中關於網路通訊的基本過程。伺服器類和請求處理類的解耦,意味著很多應用可以使用某個現有的伺服器類,而不需要其他任何的修改,只需要提供一個可以處理這些應用的請求處理類即可。但隨著近些年網路程式設計越來越複雜,對於伺服器網路通訊提出了較大的挑戰。以Web程式設計為例,挑戰主要在於:

  1. 伺服器不再僅僅提供簡單的、靜態的HTML頁面,更多的是要與豐富的Web應用進行相互通訊。如果將請求處理程式的構建放在伺服器端實現,那對於每個Web應用構建一個請求處理程式顯然不現實;
  2. 如果將請求處理程式的構建放在開發Web應用的過程中,那無疑增加了Web應用程式開發的難度和複雜度,也是不太理想的。

為了解決這些問題,常用的做法是提供一箇中間層,通常稱為閘道器介面閘道器介面在伺服器和應用中間承擔一個“翻譯官”的角色。只要應用程式符合閘道器介面的標準,那麼伺服器就只要做好伺服器的角色,應用程式只要做好應用程式的作用,伺服器和應用程式之間的通訊全靠閘道器介面來協調。常用的閘道器介面有CGIWSGI,本文就以WSGI閘道器介面來對此進行說明。

WSGI閘道器介面

WSGI (Python Web Server Gateway Interface, Python Web伺服器閘道器介面)是一個Web伺服器和Web應用程式之間的標準化介面,用於增進應用程式在不同的Web伺服器和框架之間的可移植性。關於該標準的官方說明可以參考PEP333

WSGI的主要作用是在Web伺服器和Web應用程式承擔“翻譯官”的角色。對於這一角色可以這樣理解:

  1. Web伺服器的責任在於監聽和接收請求。在處理請求的時候呼叫WSGI提供的標準化介面,將請求的資訊轉給WSGI
  2. WSGI的責任在於“中轉”請求和響應資訊。WSGI接收到Web伺服器提供的請求資訊後可以做一些處理,之後通過標準化介面呼叫Web應用,並將請求資訊傳遞給Web應用。同時,WSGI還將會處理Web應用返回的響應資訊,並通過伺服器返回給客戶端;
  3. Web應用的責任在於接收請求資訊,並且生成響應。

根據以上分析,要實現符合WSGI標準的Web服務,伺服器和應用程式的設計就要符合WSGI規範。

WSGI規範

WSGI規範如下:

  • 伺服器的請求處理程式中要呼叫符合WSGI規範的閘道器介面;
  • 閘道器介面呼叫應用程式,並且要定義start_response(status, headers)函式,用於返回響應;
  • 應用程式中實現一個函式或者一個可呼叫物件webapp(environ, start_response)。其中environ是環境設定的字典,由伺服器和WSGI閘道器介面設定,start_response是由閘道器介面定義的函式。

在Python標準庫中,wsgiref包就是符合WSGI標準的Web服務實現。後面簡單對wsgiref包進行介紹,以此來對符合WSGI標準的Web服務的實現過程進行梳理。

wsgiref包

wsgiref包為實現WSGI標準提供了一個參考,它可以作為獨立的伺服器測試和除錯應用程式。在實際的生產環境中儘量不要使用。wsgiref包含有以下模組:

  • simple_server模組 ——simple_server模組實現了可以執行單個WSGI應用的簡單的HTTP伺服器。
  • headers模組 ——管理響應首部的模組。
  • handlers模組 ——符合WSGI標準的Web服務閘道器介面實現。該模組包含了一些處理程式物件,用來設定WSGI執行環境,以便應用程式能夠在其他的Web伺服器中執行。
  • validate模組 ——“驗證包裝”模組,確保應用程式和伺服器都能夠按照WSGI標準進行操作。
  • util模組 ——一些有用的工具集。

以上模組暫時不做詳細的介紹。本文剩餘內容將simple_server模組單獨拿出來,以其中的測試例子簡單說明符合WSGI標準的Web伺服器的實現過程。

simple_server——一個簡單的符合WSGI規範的伺服器

wsgiref包的simple_server模組實現了一個符合WSGI規範的伺服器。測試程式碼如下:

1. 建立HTTP伺服器

上述測試程式碼中httpd = make_server(”, 8000, demo_app)建立了一個HTTP伺服器。

其中make_server函式用來建立伺服器:

make_server函式使用WSGIServer類構建符合WSGI規範的HTTP伺服器,使用WSGIRequestHandler類作為處理請求的類,使用demo_app作為一個Web應用。該函式返回一個伺服器例項,並開始監聽請求。可以通過httpd.socket.getsockname()獲取伺服器地址和埠號。

2. 使用webbrowser模組建立請求

緊接著,測試例子匯入webbrowser模組,使用函式建立了一個請求。

3. 伺服器處理請求

伺服器通過handle_request()方法處理請求。關於處理請求的過程簡單介紹如下:

  • handle_request()方法通過呼叫get_requestverify_requestprocess_requestfinish_request等方法建立一個請求處理例項(該過程可以參考TCPServer、HTTPServer的實現過程);
  • 請求處理例項呼叫handle()方法處理請求。handle()WSGIRequestHandler類中進行了重寫。程式碼如下:

上面handle()函式先解析了請求,之後建立了一個WSGI閘道器類例項handler,這個例項可以作為伺服器和應用程式之間的介面存在。

4. WSGI閘道器的請求處理過程

WSGI閘道器的定義在handlers模組。上一步驟中通過呼叫WSGI閘道器類例項handlerrun方法,WSGI閘道器開始處理請求。

run方法的程式碼如下:

run方法的主要功能有:

  • 通過setup_environ()方法建立WSGI相關的環境;
  • 呼叫WSGI應用的函式或者WSGI應用的可呼叫物件。本測試例子中的WSGI應用是一個簡單的函式,其作用是將請求的environ資訊列印出來。
  • 呼叫finish_response()方法將WSGI應用返回的資料作為響應發回。

5. 關閉伺服器

請求結束後,伺服器會呼叫一系列函式關閉請求連線。之後測試程式碼呼叫server_close()方法關閉伺服器。

相關文章