HTTP/1.1協議是一個基於文本的傳輸協議。傳輸報文都是直接以文本的形式傳遞消息。因此本質上講,HTTP服務器就是負責解析文本,處理請求,而後組織文本並回傳客戶端。python
Web開發剛剛興起的時候,HTTP服務器開發這塊各家都有本身的實現,有本身的特色。有些報文解析速度快,有一些處理請求速度快,有一些組織回傳結果的速度快。爲了方便代碼複用,實現這些不一樣特色的服務器模塊的按需組織,一些語言就自行定義了一些協議,對Web服務器的開發作出一些建議性的規定。好比,Java的servlet協議。git
Python則自定義了WSGI協議。github
Python官方在PEP-0333和PEP-3333中詳細定義了WSGI協議的細節。前者主要適用於Python2.x,後者根據Python3.x的語言細節變化對WSGI協議作了一些調整。數據庫
WSGI協議將整個Web服務器程序劃分爲應用框架端(後文簡稱爲應用端)、服務器網關端(後文簡稱爲服務器端)以及中間件。中間件,在應用端來看就是服務器端,在服務器端來看就是應用端。因此整體來說,WSGI就只規定了應用端和服務器端之間的交互協議。segmentfault
在WSGI協議中,服務器端負責監聽套接字端口,接收報文,解析報文,嚮應用端發送解析結果,並接收應用端的響應結果,組織響應報文,向客戶端發送響應報文。而應用端,接收到服務器端的解析結果後,根據HTTP方法、URI以及其餘報文,執行代碼,調用模版生成動態網頁,也許會向數據庫後端發送查詢請求。後端
WSGI協議的交互核心就是一個app(environ, start_response)
,是一個回調機制的實現。app()
方法由應用端實現,environ
參數是標準的Python built-in字典,其中包含了WSGI環境變量以及服務器對HTTP請求報文的解析結果;start_response
是一個相似於函數的可調用對象(任何實現了__call__
方法的實例都是可行的),由服務器端實現,負責組織響應報文。start_response
接收兩個參數status
, response_headers
。前者是應用端響應結果的狀態,例如"100 Continue"
、"200 OK"
、"404 Not Found"
;後者這是響應報文的報文頭,是一個數組,其中的每個元素又是一個元組,例如[("Content-type", "text/plain")]
。數組
WSGI協議規定的是一個同步Web框架,由於要照顧某些沒法異步實現的流程。服務器
服務器端框架直接處理網絡IO,對整個Web服務器的性能影響很是大。常見的服務器端框架有多線程、異步兩種基礎機制。網絡
bjoern是一個使用C語言實現的內存佔用少並且擁有極強性能的Python服務器端框架。bjoern使用了事件驅動框架libev、高性能HTTP解析框架http-parser。bjoern使用了Python2的C語言API,所以只能用於CPython解釋器,且不支持Python3。原做者暫時也沒有支持Python3的打算,issue: Python 3 support (PEP 3333) #29。此外bjoern不支持SSL。多線程
bjoern的異步機制是這樣的。首先註冊一個ev_io事件保持對主端口(80或者443)的監聽狀態。一旦套接字可讀,表示有新的鏈接請求,這時候當即調用回調函數,執行accept動做,並註冊另外一個ev_io事件保持對客戶端鏈接的監聽,並持續接收報文(io可讀),調用WSGI協議,持續回傳響應(io可寫)等等。接收、回傳兩部均實現事件驅動機制。
同一個ev_loop,既保持對主端口的監聽,同時保持對全部客戶端口的報文收發,對其中的全部網絡IO事件協調調度。
CherryPy是一個純Python實現的生產級高穩定高性能框架,包含了應用端和服務器端。而cheroot則是CherryPy框架解耦以後分離出來的服務器端框架,支持Python2和Python3,同時還支持PyPy。
cheroot的多線程機制簡單的說就是一個線程池。線程池使用數組維護。其中的線程進行了自定義,能夠實現線程的狀態查詢和管理。cheroot有一個主線程監聽經常使用端口,主線程會將新的鏈接請求放入一個任務隊列當中。線程池中的線程從任務隊列中獲取任務並執行其他流程。這實際上是一個經典的「生產者-消費者」模式。
CPython和PyPy都有GIL,因此線程池是一個相對低效的實現。不管是內存佔用仍是邏輯流的切換開銷,多線程機制遠比事件驅動機制的開銷要大。
網上有一篇博文對幾個Python服務器端框架進行了評測:A Performance Analysis of Python WSGI Servers: Part 2,能夠進行一下簡單的分析。