http://uwsgi-docs-cn.readthedocs.io/zh_CN/latest/WSGIquickstart.html
這個快速入門指南將會向你展示如何部署簡單的 WSGI 應用和普通 web 框架。
Python 在這里特指 CPython,如果你想用 PyPy 你需要使用專門的插件: The PyPy plugin, Jython 的支持正在開發中。
注解
為了完成這個快速入門確保你的 uWSGI 的版本在 1.4 以上。任何舊的東西 都不會再維護並且使用它們是非常危險的。
安裝帶 Python 支持的 uWSGI
小技巧
當你開始學習 uWSGI 的時候,嘗試從官方源代碼構建:使用發行版提供的包可能會 讓你非常頭疼。當事情變得明朗一點的時候,你可以使用模塊化構建(就像在你的發行版中提供的一樣)。
uWSGI 是一個(巨大的) C 應用,所以你需要一個 C 編譯器(比如 gcc 或者 clang)和 Python 開發版頭文件。
在 Debian 系的發行版上一條
apt-get install build-essential python-dev
命令就夠了。
你有多種方式來安裝 uWSGI 的 Python 包:
-
使用 pip
pip install uwsgi
-
使用網絡安裝
curl http://uwsgi.it/install | bash -s default /tmp/uwsgi
(這將會把 uWSGI 二進制文件安裝到
/tmp/uwsgi
下,你可以隨意修改它)。 -
通過下載源代碼然后 make 安裝
wget http://projects.unbit.it/downloads/uwsgi-latest.tar.gz tar zxvf uwsgi-latest.tar.gz cd <dir> make
(make 完后你會在你的當前目錄下得到一個
uwsig
的二進制文件)。
通過你的發行版的包管理器安裝是不能面面俱到的(不可能讓所有人都開心),但是一般的規則都適用。
當你使用發行版提供的包來測試這個快速入門的時候,一件你可能想重視的事情就是很有可能 你的發行版是用模塊化的方式構建的(每個特性都是一個不同的必須被加載的插件)。 為了完成這個快速入門,你必須在前面第一個例子的前面加上 --plugin python,http
選項, 以及當 HTTP 路由被移除時加上 --plugin python
選項(這可能對你沒什么用,繼續閱讀就好)。
第一個 WSGI 應用
讓我們從一個簡單的 “Hello World” 例子開始吧(這是在 Python 2.x 中,Python 3.x 需要 返回字節字符串,看下面):
def application(env, start_response): start_response('200 OK', [('Content-Type','text/html')]) return ["Hello World"]
(保存為 foobar.py
)。
正如你看到的,它由一個單獨的 Python 函數組成。它的名字是 “application”,這是 默認的函數名,uWSGI 的 Python 加載器將會搜索這個名字(但你當然可以修改它)。
Python 3.x 版本如下:
def application(env, start_response): start_response('200 OK', [('Content-Type','text/html')]) return [b"Hello World"]
把它部署到 HTTP 端口 9090
現在運行 uWSGI 來啟動一個會把請求傳遞給你的 WSGI 應用的 HTTP 服務器/路由器。
uwsgi --http :9090 --wsgi-file foobar.py
這就是全部了。
注解
當你有前端 web 服務器時不要使用 –http 選項,使用 –http-socket。繼續閱讀快速入門來理解為什么要這么做。
添加並發和監控
你想做的第一件事可能就是增加並發(uWSGI 默認啟動一個單獨的進程和一個單獨的線程)。
你可以通過 --processes
選項或者 --threads
(或者兩個選項都使用)來增加更多的進程或者線程。
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2
這將會產生 4 個進程(每個進程 2 個線程),一個主進程(當你的進程死掉時會重新 spawn 一個新的)以及 HTTP 路由器(見前面)。
一個重要的任何就是監控。知道發生了什么在生產環境中是極其重要的。stats 子系統允許你 用 JSON 輸出 uWSGI 的內部數據:
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
向你的應用發送幾個請求然后 telnet 到 9191 端口,你將得到大量有趣的信息。你可能想要使用 “uwsgitop” (使用 pip install
你就能得到它),這是一個類似 top 的工具,用於監控應用實例。
注意
將 stats 套接字(socket)綁定到私有地址(除非你知道你在做什么),否則任何人都可以訪問到它!
放到一個完整的 web 服務器后
即使 uWSGI HTTP 路由器(router)是一個可靠的高性能服務器,你可能還是想把你的應用放到一完整的 web 服務器后。
uWSGI 通常和 HTTP,FastCGI,SCGI 以及它自己特有的協議 “uwsgi” (呃,名字不應該這么取的) 通信。 性能最高的協議顯然是 uwsgi,並且早已被 nginx 和 Cherokee 支持 (同時 Apache 也有許多可用的模塊)。
一個普通的 nginx 配置如下:
location / { include uwsgi_params; uwsgi_pass 127.0.0.1:3031; }
這個意思是說 “把每個請求傳遞到服務器綁定的端口 3031,並且使用 uwsgi 協議通信”。
現在我們可以 spawn 一個 uWSGI 進程來天然地以 uwsgi 協議通信:
uwsgi --socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
如果你運行 ps aux
,你將會看到少了一個進程。HTTP 路由器(router)已經從我們的 “workers” (分配給 uWSGI 的進程) 中被移除了,這些 worker 便是天然地用來以 uwsgi 協議形式通信的。
如果你的代理/web 服務器/路由器使用 HTTP 協議,你必須告訴 uWSGI 使用 HTTP 協議(這與通過 –http spawn 一個它自己的代理是不一樣的):
uwsgi --http-socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
開機自啟動 uWSGI
如果你打算打開 vi 寫一個 init.d 腳本來啟動 uWSGI,坐下來冷靜一下然后先確保 你的系統沒有提供一個更好(更現代化)的方式。
沒一個發行版會選擇一個啟動系統 (Upstart, Systemd...),除此之外也有許多 進程管理工具(supervisord, god, monit, circus...)。
uWSGI 與上面列出的那些工具都集成得很好(我們希望如此),但是如果你想部署大量應用的話, 看看 uWSGI 的 Emperor - 它或多或少是每個開發運維工程師的夢想。
部署 Django
Django 可能是使用得最多的 Python web 框架了。部署它非常簡單(我們仍然使用 4 個進程,2 個線程的配置)。
假定你的 Django 項目在 /home/foobar/myproject
下:
uwsgi --socket 127.0.0.1:3031 --chdir /home/foobar/myproject/ --wsgi-file myproject/wsgi.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
(通過 --chdir
選項我們可以移動一個特定的目錄)。在 Django 中為了正確的加載模塊這是必須的。
啊!這是什么鬼?!是的,你是對的,你是對的。。。處理這么長的命令行是不實際的,又蠢又容易出錯。 不要怕! uWSGI 提供多種配置風格。在這個快速入門里我們將使用 .ini 文件。
[uwsgi]
socket = 127.0.0.1:3031 chdir = /home/foobar/myproject/ wsgi-file = myproject/wsgi.py processes = 4 threads = 2 stats = 127.0.0.1:9191
更好一點了!
盡管運行它:
uwsgi yourfile.ini
如果 /home/foobar/myproject/myproject/wsgi.py
(或者其他你的項目的名字) 這個文件不存在,你很有可能 使用的是老的版本的 Django (1.4 以下)。在這種情況下你需要配置更多一點的東西:
uwsgi --socket 127.0.0.1:3031 --chdir /home/foobar/myproject/ --pythonpath .. --env DJANGO_SETTINGS_MODULE=myproject.settings --module "django.core.handlers.wsgi:WSGIHandler()" --processes 4 --threads 2 --stats 127.0.0.1:9191
或者,使用 .ini 文件:
[uwsgi]
socket = 127.0.0.1:3031 chdir = /home/foobar/myproject/ pythonpath = .. env = DJANGO_SETTINGS_MODULE=myproject.settings module = django.core.handlers.wsgi:WSGIHandler() processes = 4 threads = 2 stats = 127.0.0.1:9191
老版(1.4 以下)的 Django 發行版需要設置 evn
, module
和 pythonpath
(..
使得我們可以訪問myproject.settings
模塊)。
部署 Flask
Flask 是一個流行的 Python web 微框架。
保存下面這個例子到 myflaskapp.py
:
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return "<span style='color:red'>I am app 1</span>"
Flask 把它的 WSGI 函數(就是我們在之前快速入門里稱作 “application” 即應用的東西)暴露成 “app”, 所以 我們需要告訴 uWSGI 去使用它。 我們仍然使用 4 個進程/2 個線程,以及 uwsgi socket :
uwsgi --socket 127.0.0.1:3031 --wsgi-file myflaskapp.py --callable app --processes 4 --threads 2 --stats 127.0.0.1:9191
(唯一增加的選項便是 --callable
選項)。
部署 web2py
又是一個流行的選擇。你可以選擇把 web2py 的發行版源代碼解壓到一個目錄然后寫一個 uWSGI 配置文件:
[uwsgi]
http = :9090 chdir = path_to_web2py module = wsgihandler master = true processes = 8
注解
On recent web2py releases you may need to copy the wsgihandler.py
script out of the handlers
directory.
我們再次使用 HTTP 路由器(router)。用你的瀏覽器訪問 9090 端口然后你就可以看到 web2py 的歡迎頁面了。
點擊管理頁面然后...哎呀,它需要 HTTPS。不要擔心,uWSGI 路由器(router)可支持 HTTPS (確保你 有 OpenSSL 開發版的頭文件:安裝它們然后重新構建 uWSGI,build 系統會自動檢測到它)。
First of all generate your key and certificate: 首先生成你的秘鑰(key)和證書(certificate):
openssl genrsa -out foobar.key 2048
openssl req -new -key foobar.key -out foobar.csr
openssl x509 -req -days 365 -in foobar.csr -signkey foobar.key -out foobar.crt
現在你有兩個文件(算上 foobar.csr
的話就是三個了), foobar.key
和 foobar.crt
。修改 uWSGI 配置:
[uwsgi]
https = :9090,foobar.crt,foobar.key chdir = path_to_web2py module = wsgihandler master = true processes = 8
重新運行 uWSGI 然后使用 https://
用你的瀏覽器連接到 9090 端口。
Python 線程小貼士
如果你沒有使用線程啟動 uWSGI,Python 的 GIL 將不會被開啟,所以你的應用產生的線程 將永遠不會運行。你可能不會喜歡這個選擇,但是記住 uWSGI 是一個語言無關的服務器,所以它的 大部分選擇都是盡可能維持它 “agnostic”。
但是不用擔心,基本上不存在不能通過選項來改變的由 uWSGI 開發者決定的選項。
如果你想維持 Python 的線程支持同時應用又不啟動多個線程,只需要加上 --enable-threads
選項 (或者 enable-threads = true
在 ini 風格配置文件中)。
Virtualenvs
uWSGI 可以被配置成在某個特定的 virtualenv 中搜索 Python 模塊。
只要添加 virtualenv = <path>
到你的選中中就可以了。
安全和可用性
永遠 不要使用 root 來運行 uWSGI 實例。你可以用 uid
和 gid
選項來降低權限:
[uwsgi]
https = :9090,foobar.crt,foobar.key uid = foo gid = bar chdir = path_to_web2py module = wsgihandler master = true processes = 8
如果你需要綁定到一個特權端口(比如 HTTPS 的443),使用共享套接字(shared sockets)。它們在權限降低之前被創建,可以 使用 =N
語法來引用,這里的 N
指 socket 編號(從0開始):
[uwsgi]
shared-socket = :443 https = =0,foobar.crt,foobar.key uid = foo gid = bar chdir = path_to_web2py module = wsgihandler master = true processes = 8
web 應用開發一個最常見的問題就是 “stuck requests”(卡住的請求)。你所有的線程/worker 都被卡住(被請求堵塞), 然后你的應用再也不能接受更多的請求。 為了避免這個問題你可以設置一個 harakiri
計時器。它是一個監視器(由主進程管理),當 進程被卡住的時間超過特定的秒數后就銷毀這個進程(慎重選擇 harakiri
的值)。比如,你可能 想把卡住超過 30 秒的 worker 銷毀掉:
[uwsgi]
shared-socket = :443 https = =0,foobar.crt,foobar.key uid = foo gid = bar chdir = path_to_web2py module = wsgihandler master = true processes = 8 harakiri = 30
另外,從 uWSGI 1.9 起,統計服務器會輸出所有的請求變量,所以你可以(實時地)查看你的 實例在干什么(對於每個 worker,線程或者異步 core)。
Offloading
The uWSGI offloading subsystem 使得你可以在某些模式滿足時釋放你的 worker,並且把工作委托給一個純 c 的線程。 這樣例子比如有從文件系統傳遞靜態文件,通過網絡向客戶端傳輸數據等等。
Offloading 非常復雜,但它的使用對用戶來說是透明的。如果你想試試的話加上 --offload-threads <n>
選項,這里的 <n> 是 spawn 的線程數(以 CPU 數目的線程數啟動是一個不錯的值)。
當 offload threads 被啟用時,所有可以被優化的部分都可以自動被檢測到。
Bonus: 多版本 Python 使用同一個 uWSGI 二進制文件
正如我們已經看到的,uWSGI 由一個很小的核心和許多插件組成。插件可以被嵌入到二進制文件中 或者動態加載。當你為 Python 構建 uWSGI 的時候,許多插件包括 Python 在內的插件都被嵌入到了最終的二進制文件中。
當你使用多個 Python 版本但是沒有為每一個版本構建一個二進制文件時這可能會造成問題。
最好的方法可能是弄一個沒有內置語言特性的小二進制文件,然后每個 Python 版本有一個 插件,可以動態地加載。
在 uWSGI 的源代碼目錄中:
make PROFILE=nolang
這將會構建一個包含除了 Python 之外的所有默認內置插件的 uwsgi 二進制文件。
現在,在相同的目錄下,我們開始構建 Python 插件:
PYTHON=python3.4 ./uwsgi --build-plugin "plugins/python python34" PYTHON=python2.7 ./uwsgi --build-plugin "plugins/python python27" PYTHON=python2.6 ./uwsgi --build-plugin "plugins/python python26"
你最后會得到這些文件: python34_plugin.so
, python27_plugin.so
, python26_plugin.so
。復制 這些文件到你的目錄中。(uWSGI 默認在當前的工作目錄中搜索插件。)
現在你只需要在你的配置文件中(在文件最上面)簡單加上 plugins-dir 和 plugin 選項就可以了。
[uwsgi]
plugins-dir = <path_to_your_plugin_directory> plugin = python26
這將會從你復制插件到的那個目錄中加載 python26_plugin.so
插件。
那么現在...
有了這些很少的概念你就已經可以進入到生產中了,但是 uWSGI 是一個擁有上百個特性和配置的生態系統。 如果你想成為一個更好的系統管理員,繼續閱讀完整的文檔吧。