天天看點

werkzeug 詳解

首先,先向大家介紹一下什麼是 werkzeug,Werkzeug是一個WSGI工具包,他可以作為一個Web架構的底層庫。這裡稍微說一下, werkzeug 不是一個web伺服器,也不是一個web架構,而是一個工具包,官方的介紹說是一個 WSGI 工具包,它可以作為一個 Web 架構的底層庫,因為它封裝好了很多 Web 架構的東西,例如 Request,Response 等等。

例如我最常用的 Flask 架構就是一 Werkzeug 為基礎開發的,這也是我要解析一下 Werkzeug 底層的原因,因為我想知道 Flask 的實作邏輯以及底層控制。這篇文章沒有涉及到 Flask 的相關内容,隻是以 Werkzeug 建立一個簡單的 Web 應用,然後以這個 Web 應用為例剖析請求的處理以及響應的産生過程。

下面我們以一個簡短的例子開始,先看看怎麼使用 werkzeug,然後再逐漸刨析 werkzeug 的實作原理。

安裝 werkzeug

我希望讀者是在 virtualenv 環境中跟着我的步伐走得,如果你還不知道什麼是 virtualenv,那麼你可以在我的部落格中搜尋一下 virtualenv,然後先弄好,再繼續,因為很可能因為一些庫的沖突等問題導緻你看不到本文中介紹的東西。

ok,下面開始安裝 werkzeug,

1 pip install Werkzeug

這條指令下去,幾秒鐘之後你就可以使用 werkzeug 了。

一個簡單地 web 伺服器

接下來,我們就開始使用 werkzeug 來建立一個簡單的 web 伺服器,這個伺服器就僅僅傳回 “Hello Werkzeug”,沒有其他内容。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #!/usr/bin/env python # encoding: utf-8 import os   from werkzeug . serving import run_simple from werkzeug . wrappers import Request , Response from werkzeug . wsgi import SharedDataMiddleware   class Shortly ( object ) :      def dispatch_request ( self , request ) :          return Response ( 'Hello Werkzeug!' )        def wsgi_app ( self , environ , start_response ) :          request = Request ( environ )          response = self . dispatch_request ( request )          return response ( environ , start_response )        def __call__ ( self , environ , start_response ) :          return self . wsgi_app ( environ , start_response )   def create_app ( with_static = True ) :      app = Shortly ( )      if with_static :          app . wsgi_app = SharedDataMiddleware ( app . wsgi_app , {              '/static' : os . path . join ( os . path . dirname ( __file__ ) , 'static' )          } )      return app   if __name__ == '__main__' :      app = create_app ( )      run_simple ( '127.0.0.1' , 6666 , app , use_debugger = True , use_reloader = True )

這段代碼就實作了我說的功能,那麼我們就來看看這段代碼是怎麼運作的?

首先,一切都回到最開始的地方開始,從 main 開始看起,可以發現 main 是非常簡單地,隻有一個初始化函數,然後就調用了 werkzeug 的 run_simple 函數。okay,我們可以發現這個 app 其實是一個 Shortly 對象,這個類就隻實作了 3 個方法,一個是 dispatch_request, wsig_app, call ,就這麼簡單了,那我們就知道了,關鍵的代碼都不是這些,應該是 run_simple.

run_simple 解析

okay,我們這個系列部落格的目的就是解析 werkzeug 源碼,是以拿到 werkzeug 源碼肯定是我們必須要做的。是以第一步我們就需要從 github 上将 werkzeug clone 下來:

1 git clone https : //github.com/mitsuhiko/werkzeug.git

然後,我們就找 run_simple 的代碼咯

1 vim werkzeug / serving . py

goto line 559

我們可以看到這個函數的定義,秉着關注重點的原則,我們就忽略條件判斷,以一條最簡單地路線來看代碼,那麼這裡就假設:

1 2 3 use_debugger = False static_files = False use_reloader = False

OK, 那到這裡其實 run_simple 調用的就是 inner 了,那麼就來看看 inner 的代碼:

1 2 3 4 5 6 7 8 9 10 11 646 : try : 647 :    fd = int ( os . environ [ 'WERKZEUG_SERVER_FD' ] ) 648 : except ( LookupError , ValueError ) : 649 :    fd = None 650 : srv = make_server ( hostname , port , application , threaded , 651 :                    processes , request_handler , 652 :                    passthrough_errors , ssl_context , 653 :                    fd = fd ) 654 : if fd is None : 655 :    log_startup ( srv . socket ) 656 : srv . serve_forever ( )

忽略 fd,那麼剩下一點點了:

1 2 3 4 5 6 650 : srv = make_server ( hostname , port , application , threaded , 651 :                      processes , request_handler , 652 :                    passthrough_errors , ssl_context , 653 :                    fd = fd )   656 : srv . serve_forever ( )

好,你應該和我一樣有興緻得想知道這個 make_server 裡面是什麼内容了,我也很期待,那就跟上去看看。

make_server 的代碼我就不貼了,還是最簡原則,忽略各種條件,那麼這裡就假設:

1 2 threaded = False processes = 1

那麼代碼也很簡單了,就剩下:

1 2 546 : return BaseWSGIServer ( host , port , app , request_handler , 547 :                        passthrough_errors , ssl_context , fd = fd )

很好,好不容易跟蹤到這,終于上關鍵了,那就是這個 BaseWSGIServer 了,我們就來看看這個類實作了什麼功能。

先看這個類的定義:

1 443 : class BaseWSGIServer ( HTTPServer , object ) :

這個類是繼承自 HTTPServer 的,那麼我們就有點底了,這差不多到頭了,已經和 Python 的 API 碰上了。好,既然是繼承自 HTTPServer,那麼就把他當做 HTTPServer,然後繼續看 run_simple 的代碼,我們一路跟蹤下來,我們發現了 656 行有一個 

srv.serve_forever()

,那麼這不就是 HTTPServer 的用法嗎? server.serve_forever() 。

okay,到這那麼事情已經暫告一段落了,雖然很多事情都還沒搞清楚,例如請求是怎麼被封裝的,響應又在哪裡被處理了,例如URL路由之類的怎麼操作的。但是,我們已經對 Werkzeug 有一個大概的印象了,知道他低層還是 HTTPServer 實作的,沒有太多特殊的自定義協定。在下一章我們會逐漸得進行進行更深層次的解密。歡迎繼續關注。