聊聊Python的Web服務器框架(一)

HTTP/1.1協議是一個基於文本的傳輸協議。傳輸報文都是直接以文本的形式傳遞消息。因此本質上講,HTTP服務器就是負責解析文本,處理請求,而後組織文本並回傳客戶端。python

Web開發剛剛興起的時候,HTTP服務器開發這塊各家都有本身的實現,有本身的特色。有些報文解析速度快,有一些處理請求速度快,有一些組織回傳結果的速度快。爲了方便代碼複用,實現這些不一樣特色的服務器模塊的按需組織,一些語言就自行定義了一些協議,對Web服務器的開發作出一些建議性的規定。好比,Java的servlet協議。git

Python則自定義了WSGI協議。github

WSGI協議

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的異步機制

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事件協調調度。

cheroot的多線程機制

CherryPy是一個純Python實現的生產級高穩定高性能框架,包含了應用端和服務器端。而cheroot則是CherryPy框架解耦以後分離出來的服務器端框架,支持Python2和Python3,同時還支持PyPy。

cheroot的多線程機制簡單的說就是一個線程池。線程池使用數組維護。其中的線程進行了自定義,能夠實現線程的狀態查詢和管理。cheroot有一個主線程監聽經常使用端口,主線程會將新的鏈接請求放入一個任務隊列當中。線程池中的線程從任務隊列中獲取任務並執行其他流程。這實際上是一個經典的「生產者-消費者」模式。

CPython和PyPy都有GIL,因此線程池是一個相對低效的實現。不管是內存佔用仍是邏輯流的切換開銷,多線程機制遠比事件驅動機制的開銷要大。

看看評測

網上有一篇博文對幾個Python服務器端框架進行了評測:A Performance Analysis of Python WSGI Servers: Part 2,能夠進行一下簡單的分析。

  • 併發處理能力:bjoern優點太明顯,顯得好像作過弊(固然了,原文已經排除這一可能)。這能夠歸功於純C語言的實現和libev。驚喜的是,CherryPy的併發處理能力竟然超過了meinheld。後者是部分使用C語言實現的,基於http-parser框架和picoev框架,一個原做者稱比libev更適合網絡IO的事件驅動框架。按照bjoern和meinheld的官方文檔描述,二者具備類似的結構設計,計算密集型流程使用的性能相近的第三方框架,因此二者應該水平至關。我尚未看meinheld的源碼,不瞭解具體緣由。 固然了,此次評測沒有模擬數據庫請求IO過程,因此和真實環境的併發並不徹底同樣。
    每秒處理請求-All
    每秒處理請求-排除bjoern
  • 處理延遲:同時鏈接數不斷增長時,CherryPy的處理延遲很是低,幾乎爲一條水平線。看上去,GIL並無構成瓶頸。bjoern隨着同時鏈接數的增長,處理延遲略有上升,但斜率很低。這裏的處理延遲,表示的是從接收請求到發送響應之間的時間差。本次測試的應用端不涉及模版處理或網頁生成,而是直接返回預設參數。
    處理延遲
  • 內存佔用:內存佔用是異步框架的強項。bjoern,和meinheld一塊兒處於第一梯隊,並和其餘框架拉開了巨大的差距。
    內存佔用
  • 異常:異常是指服務器忽然斷開鏈接或者鏈接超時。這個測試裏,CherryPy展現出強大的實力,幾乎爲0。bjoern也不錯,但同時鏈接數很高的時候,表現開始不那麼穩定。
    異常
  • CPU佔用:這個因不一樣的底層機制而異。
    CPU佔用

參考閱讀

  1. PEP 333 -- Python Web Server Gateway Interface v1.0
  2. PEP 3333 -- Python Web Server Gateway Interface v1.0.1
  3. WSGI簡介
相關文章
相關標籤/搜索