Python工具包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 實現的,沒有太多特殊的自定義協議。在下一章我們會逐步得進行進行更深層次的解密。歡迎繼續關注。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章