最新的工做中,有一部分HTTP API的任務,因而開始折騰Python WSGI...python
先惡補理論吧,關於Python WSGI有2篇PEP須要看,PEP 0333和PEP 3333,前者是2003年的提案,後者在2010年對前者作了小幅修訂,提案狀態也已是「Final」,因此已經不是「提案」,已是協議規範了;flask
WSGI,是「Python Web Server Gateway Interface」的縮寫,解決的是各類Web Server(好比Apache、Nginx)與各類Python Web框架(好比Flask、Tornado)之間互聯互通的兼容性問題;vim
如PEP中描述,在沒有一個統一協議規範之前,某個Python應用(Web框架、或者應用程序),可能僅支持運行在某一個Web Server下,應用開發者選擇某個Web框架的同時,也就綁死了某個Web Server;另外,在某個Web框架寫的應用代碼,遷移到另一個Web框架,不必定能跑起來;因此,爲了能讓用戶自由選擇、組合使用任意的Web框架和Web Server,該PEP規範了必要的交互方式和數據結構;瀏覽器
注意,WSGI不是一個軟件工具,它是一種協議規範的描述,它規範了「交互方式和數據結構」,任何Web Server、Gateway和Framework、Application,只要聽從這個規範去實現,就必定能互聯互通;既然是「交互」,就必定至少包含兩方吧,PEP指出:bash
「application端」,必須提供一個「可調用的對象」供「server端」調用,「可調用的對象」通常是個入口函數,應用對HTTP請求的處理,要在這個入口函數內完成,函數最終返回HTTP Respone,好比生成的HTML;數據結構
下面的示例摘自PEP 3333:架構
pythonHELLO_WORLD = b"Hello world!\n" def simple_app(environ, start_response): """Simplest possible application object""" status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return [HELLO_WORLD]
「server端」,通常就是Web Server啦,最主要的任務就是接受到HTTP請求後,調用「application端提供的入口函數(好比上面的「simple_app」),另外應用程序裏可能須要不少HTTP請求的信息(好比HTTP Method,GET/POST),這些信息,如何獲取呢?須要「server端」在調用應用入口函數的時候做爲函數參數傳遞,就是上面的「environ」,關於「environ」這個數據結構的規範,PEP中也是有詳細描述的;app
關於「server端」實現的實例代碼,稍稍複雜一點,搬運到這裏,也不見得幫助理解,自行到PEP 3333查閱吧;框架
關於Python WSGI的理論介紹,最核心的都在這兒了,更細節的協議介紹,自行查閱吧;python2.7
好了,終於能夠動手實踐了,早已按耐不住啦!咱們先把上面的「simple_app」跑起來吧;
bash$ touch simple_app.py $ vim simple_app.py HELLO_WORLD = b"Hello world!\n" def application(environ, start_response): """Simplest possible application object""" status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return [HELLO_WORLD]
好了,「application端」已經好了,那「server端」呢?咱們須要搞來一個實現了Python WSGI的Web Server,這裏,我選擇了,uWSGI,不要被它的名字迷惑,它實際上是一個功能很是全、實現了各類協議、支持各類的語言的「a full stack for building hosting services」,由於最先支持Python,因此取了這樣一個名字,官方都說,名字已經支撐不起它的野心,哈哈;
本實踐中,咱們要基於Python安裝uWSGI:
bash$ pip install uwsgi
根據你的Python安裝路徑,uWSGI的二進制程序安裝路徑也會不一樣,比我電腦上:
bash$ ls /usr/local/python2.7/bin/uwsgi
如今,「application端」和「server端」,都已經準備就緒了,咱們能夠啓動的這個簡單的WSGI應用了:
bashuwsgi --http :9090 --wsgi-file simple_app.py
uWSGI會到simple_app.py文件中,找一個固定名字「application」的入口函數,當uWSGI收到HTTP請求時,就會調用該入口函數;
你在瀏覽器請求http://127.0.0.1:9090,應該會看到uWSGI的輸出:
bash$ uwsgi --http :9090 --wsgi-file simple_app.py [pid: 1492|app: 0|req: 1/1] 127.0.0.1 () {34 vars in 626 bytes} [Sat Jun 20 15:32:14 2015] GET / => generated 11 bytes in 0 msecs (HTTP/1.1 200) 1 headers in 44 bytes (1 switches on core 0) [pid: 1492|app: 0|req: 2/2] 127.0.0.1 () {36 vars in 615 bytes} [Sat Jun 20 15:32:14 2015] GET /favicon.ico => generated 11 bytes in 0 msecs (HTTP/1.1 200) 1 headers in 44 bytes (1 switches on core 0)
此時,你的瀏覽器應該也已經有「Hello World」頁面輸出,大功告成!
本文的「Hello World」示例,僅僅是展現Python WSGI的框架結構,「server端」提供的HTTP請求信息「environ」徹底沒有使用,無腦返回一個不變的字符串;
真正的應用,是須要根據功能要求,定製、填充那個application的入口函數的,應用開發者,能夠本身代碼實現,也能夠依託於一些流行的WSGI框架和Python模塊,好比:Flask框架、werkzeug
「server端」就沒有什麼發揮的空間了,選擇一個支持WSGI協議的就行了,就像本文嘗試的uWSGI,除非你想本身實現個Web Server;Nginx雖然不直接支持WSGI,但它支持uWSGI,這樣把Nginx擋在uWSGI前面,uWSGI就能夠專門處理動態的WSGI請求了,造成的架構,很是相似於PHP場景下的Nginx+PHP-FPM;
這些方面的知識,也許會有後續的文章作介紹;