webpy原始碼分析概覽圖

發表於2016-02-01

今天花了點時間把看了web.py的程式碼分析了一遍,稍稍的總結成一個圖片,供有興趣的人蔘考。

原因

在開始之前先來說下分析它程式碼的原因,昨天是打算給wechat這個專案加上異常處理,可是發現在伺服器返回400錯誤之後,客戶端獲取到得responseText和我伺服器端定義的不一樣,我伺服器端是這麼返回錯誤的: return web.BadRequest(message="使用者名稱或密碼錯誤") ,於是去檢視了下這個BadRequest是怎麼處理這個message的,發現web.py的所有http狀態的返回都是基於Exception來做的(包括你想直接返回200,可以通過 web.OK 來返回。

研究到最後發現,既然是基於Exception來做的,那麼就直接return是不對的,應該用raise才行——raise web.OK 或者 raise web.BadRequest(message="blabla..") 。

這部分對應的處理程式碼在application.py#287行:

意識到這個問題的時候,我已經通過自己的方式改造成功了,就是在web.safestr中對物件的data進行判斷,當然有什麼副作用不知道,這麼改造:

當然,最佳的方式還是要用raise來做。

原始碼分析開始

不管目的是什麼,反正最後還是把關鍵程式碼通讀了下,整理成下面這個圖,不是很詳細,但對於想分析的人來說應該會有些幫助:

http://the5fireblog.b0.upaiyun.com/staticfile/webpy-source-analyze.jpg

整個流程是從專案的啟動開始的。

application.py

先從application.py開始執行,這是使用webpy開發是簡單執行專案的入口,簡單的例子就是:

這裡的application就是圖中最上面的那個類,我把類的關鍵屬性和方法都寫了出來。

app.run() 中會執行 wsgifunc(self, *middleware) 這個方法,在這個方法裡定義了一個 wsgi 的方法(上面貼得那段異常處理的程式碼就是wsgi中的),最後被傳遞出去給到 wsgi.runwsgi` 的引數中。

httpserver.py

這個 runwsgi 是在wsgi.py中定義的,作用僅僅是呼叫httpserver的runsimple方法,並把前面的那個 wsgi 傳遞進去。

這時執行到了httpserver.py的runsimple這個函式中,在此函式中,首先是呼叫WSGIServer這個函式生成一個server例項,然後在啟動這個server。

在WSGIServer中主要工作是例項化wsgiserver包中的 CherryPyWSGIServer 這個類。順著圖再往下看 CherryPyWSGIServer 是繼承自HTTPServer的。在CherryPyWSGIServer的初始化中首先是建立了一個self.requests的執行緒池,用來存放所有的請求連線。然後是把上面的 wsgi 賦給屬性 wsgi_app ,還有就是宣告閘道器 gateway 這個用來把應用生成的資料最終返回給客戶端的元件。

wsgiserver/__init__.py

在CherryPyWSGIServer初始化是會建立一個self.requests的執行緒池,這個執行緒池在初始化時會持有這個CherryPyWSGIServer物件的例項。在上面說的啟動server時——server.start(),主要工作是繫結要監聽的地址和埠,然後啟動self.request這個執行緒池。執行緒池在啟動的時候會根據在建立requests這個執行緒池時指明執行緒池的大小來建立用來具體幹活的 WorkerThread (這個每個Worker也都會持有Server的例項),並啟動這些WorkerThread。

這些WorkerThead啟動之後會等待,從server的執行緒池的佇列中(也就是 self.server.requests.get() )獲取來自客戶端的連線 conn 。在上面的HTTPServer的定義中還有一個tick的方法,這個方法就是用來接收來自客戶端的請求,然後建立連線——HTTPConnection,最後把例項conn放到requests執行緒池中的佇列中。

在WorkerThead啟動之後會呼叫conn——HTTPConnection的 communicate 方法。在這個方法中會生成一個HTTPRequest物件,做完一些驗證和資料轉換之後(根據HTTP協議,把資料放到物件中),會呼叫這個HTTPRequest例項的respond方法,這個respond方法中,關鍵的一句是: self.server.gateway(self).respond() ——呼叫server中gateway的方法(注意:全域性只有一個CherryPyWSGIServer的例項server)。

WSGIGateway_10

上面的那句呼叫gateway中respond方法的程式碼邏輯是:例項化gateway——WSGIGateway_10,呼叫respond。在WSGIGateway_10的respond中,又通過request呼叫server上的wsgi_app方法:

通過這個生成response,最後寫回到客戶端(瀏覽器)。

到此為止就是上面圖片中要介紹的所有流程了。

最後補充下那個被傳遞到最後的wsgi_app程式碼,我一直覺得一個類或者函式被傳遞這麼多層最後來執行有點彆扭,就像是在寫JavaScript中時有很深的回撥一樣難以理解。

這麼一個函式翻山涉水被傳遞到WSGI中實屬不易,理解了這個函式的傳遞過程,和這個函式的作用,基本上也就理解了webpy了。

相關文章