捉摸Python的WSGI(轉)


 過去的這個月,接觸的最多的就是Python的WSGI了,WSGI不是框架不是模塊,僅僅是一個規范協議,定義了一些接口,卻影響着Python網絡開發的方方面面。對於WSGI有這么一段定義:WSGI is the Web Server Gateway Interface. It is a specification for web servers and application servers to communicate with web applications (though it can also be used for more than that).我想我這篇文章不是詳細介紹WSGI內容的,只是想扯扯我對WSGI相關的學習。

 

      誠如那個WSGI的定義所說的,協議定義了一套接口來實現服務器端與應用端通信的規范化(或者說是統一化)。這是怎樣的一套接口呢?很簡單,尤其是對於應用端。

 

      應用端只需要實現一個接受兩個參數的,含有__call__方法的,返回一個可遍歷的含有零個或多個string結果的Python對象(我強調說Python對象,只是想和Java的對象區別開,在Python里一個方法、一個類型……都是對象,Python是真“一切皆對象”,詳見《Python源碼分析》)即可。碼農都知道,傳入參數的名字可以任意取,這里也不例外,但習慣把第一個參數命名為“environ”,第二個為“start_response”。至於這個對象的內容怎樣,應用自由發揮去吧……

 

      服務器端要做的也不復雜,就是對於每一個來訪的請求,調用一次應用端“注冊”的那個協議規定應用端必須要實現的對象,然后返回相應的響應消息。這樣一次服務器端與應用端的通信也就完成了,一次對用戶請求的處理也隨之完成了!當然了,既然協議規定了服務器端在調用的時候要傳遞兩個參數,自然也規定了這兩個參數的一些細節。比如第一個參數其實就是一個字典對象,里面是所有從用戶請求和服務器環境變量中獲取的信息內容,協議當然會定義一些必須有的值,及這些值對應的變量名;第二個參數其實就是一個回調函數,它向應用端傳遞一個用來生成響應內容體的write對象,這個對象也是有__call__方法的。

 

      協議也提到了,還可以設計中間件來連接服務器端與應用端,來實現一些通用的功能,比如session、routing等。

 

具體怎么應用這個協議呢?Python自帶的wsgiref模塊有個簡單的例子:

 

Python代碼 
  1. from wsgiref.simple_server import make_server  
  2.   
  3. def hello_world_app(environ, start_response):  
  4.     status = '200 OK' # HTTP Status  
  5.     headers = [('Content-type''text/plain')] # HTTP Headers  
  6.     start_response(status, headers)  
  7.   
  8.     # The returned object is going to be printed  
  9.     return ["Hello World"]  
  10.   
  11. httpd = make_server(''8000, hello_world_app)  
  12. print "Serving on port 8000..."  
  13.   
  14. # Serve until process is killed  
  15. httpd.serve_forever()  

 

 

     這個例子更多體現的是應用端的開發方法,很簡單的按照協議實現一個了滿足規范的方法,這樣當瀏覽器向本機8000端口發起一個請求時,就會得到一個“Hello World”的字符串文本響應。這個例子雖然簡單,但非常清楚的說明了應用端與服務器端的接口應用方式。


     你可能會想到:現在對該端口的不同地址的請求都是由這個“hello_world_app”函數處理的,你可以實現一個功能,解析一下請求的PATH信息,針對不同的地址轉發給不同的函數或是類來處理;你可能會覺得使用environ和start_response這兩個參數不直觀,你可以像Java的servlet那樣自己封裝成兩個request和response對象來用;你覺得有些常用功能可以提取出來,在具體應用邏輯之外來做……哈哈,那你就已經在思考怎么做中間件或是Web框架了!其實這些也都有人做過了,比如Routes、WebOb、Beaker……當然你大可以自己造自己獨有的輪子,有時候自己做過一遍了才會對現有的成熟的東西有更好的理解,最重要的是在Python的世界里這些都不難做到!

 

     不知你是不是和我一樣,在寫應用的時候或多或少的會想一下服務器端是怎么運作的呢?可能最模糊的流程大家都能想得到:服務器開一個socket等待客戶端連接;請求來了,服務器會讀出傳來的數據,然后根據HTTP協議做一些初步的封裝,接着就可以調用事先注冊的應用程序了,並將請求的數據塞進去;等響應處理完畢了再把數據通過socket發出去,over。好在Python的代碼簡潔,而自帶的wsgiref中的simple server也很簡單,就讓我們探究一下更具體的實現吧!

 

      首先看一下類的繼承關系,這個simple server真正的類是WSGIServer,繼承自HTTPServer,HTTPServer類又繼承自TCPServer,TCPServer又繼承自BaseServer;與server類直接打交道的還有RequestHandler類,從最上層的WSGIRequestHandler —> BaseHTTPRequestHandler —> StreamRequestHandler —> BaseRequestHandler。相對Java而言不是很復雜吧,它們是怎么工作的呢?容我稍微解釋一下。


     讓我們從Server的最基類BaseServer看起。它有一段注釋非常清楚的介紹了它定義的方法的用處:

 

Python代碼 
  1. Methods for the caller:  
  2.   
  3. - __init__(server_address, RequestHandlerClass)  
  4. - serve_forever()  
  5. - handle_request()  # if you do not use serve_forever()  
  6. - fileno() -> int   # for select()  
  7.   
  8. Methods that may be overridden:  
  9.   
  10. - server_bind()  
  11. - server_activate()  
  12. - get_request() -> request, client_address  
  13. - verify_request(request, client_address)  
  14. - server_close()  
  15. - process_request(request, client_address)  
  16. - close_request(request)  
  17. - handle_error()  
  18.   
  19. Methods for derived classes:  
  20.   
  21. - finish_request(request, client_address)  

 

         可見,一個server類其實就這么幾個方法。


      在可以被外部調用的四個方法中,構造方法顯然就是用來創建實例的;第四個可能是和構建異步服務器有關的,這里就略過了;從具體的代碼可以看到,剩下兩個方法的用處是相同的,就是處理收到的請求,只是serve_forever()方法會在server進程存在期間循環處理,而handle_request()處理一次就退出了(其實server_forever()就是循環調用了handle_request())。在handle_request()中說明了具體的從接受到返回一個請求的全部流程,代碼也很簡單:

 

Python代碼 
  1. def handle_request(self):  
  2.         """Handle one request, possibly blocking."""  
  3.         try:  
  4.             request, client_address = self.get_request()  
  5.         except socket.error:  
  6.             return  
  7.         if self.verify_request(request, client_address):  
  8.             try:  
  9.                 self.process_request(request, client_address)  
  10.             except:  
  11.                 self.handle_error(request, client_address)  
  12.                 self.close_request(request)  

 

 

     BaseServer雖然定義了這些內部調用的方法,但內容基本都是空的,留給了具體的Server類去實現。從BaseServer的代碼中就可以看到RequestHandler類的用處了,它是具體的解析了request的內容,它由finish_request()調用,而這個finsh_request()方法顯然應該是在process_request()方法中被調用的。


     TCPServer繼承BaseServer類,它真正具體化了我們猜測的socket連接的初始化過程。


     在與上面兩個類相同的源文件中,還有兩個主要的類:ThreadingMixIn和ForkingMixIn,這兩個類分別重載了process_request()方法,並且相應使用了新建一個線程或是進程的方式來調用finish_request()方法。這也從應用的角度解釋了為什么要在finish_request()外套一層process_request(),而不是直接在handle_request()的第二個try塊中調用。

 

     HTTPServer其實做的工作很簡單,就是記錄了socket server的名字。

 

     接下來就該看看WSGIServer了。它做了兩件新的工作:設置了一些基本的環境變量值,並且接受應用程序的注冊。從這個Server的代碼可以看出,應用端實現的那個接口就是從這里注冊到服務器端的,而且只能注冊一個哦!所以要有多個應用只能通過routing的方式來轉發調用了。而且這個WSGIServer不是多線程或是多進程的~

 

     至於具體封裝請求內容的RequestHandler類就不打算分析了,感興趣的話,看官們自個看一下源碼吧,也很簡單哦!下一篇博客打算分享一下我對pylons框架的運行過程的學習。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM