使用 uWSGI 建置 Django 應用
相關文章:
- 推薦:花了兩個星期,我終於把 WSGI 給搞明白了
- Django建置時為什麼要用 uWSGI與 Nginx? 以及 WSGI,uwsgi等協定
- 如何用 uWSGI 託管 Django
- uwsgi啟動django專案
- 詳解django中的collectstatic命令以及STATIC_URL、STATIC_ROOT配置
文章目錄
- 使用 uWSGI 建置 Django 應用
-
- 1. 為什麼要用 uWSGI 建置
-
- 1. WSGI 協定, uwsgi 協定:
- 2. wsgiref 模組(django 框架自帶)
- 3. uWSGI伺服器
- 4. Django 本身提供了 runserver,為什麼不能用來建置
- 5. 是否使用 Nginx 來做負載均衡
- 2. uWSGI 的安裝:
-
- 1. yum 安裝(最快捷的方式)
- 2. 原始碼安裝
- 3. uWSGI 安裝不了
- 3. uWSGI 快速教學
-
- The first WSGI application
- Deploy it on HTTP port 9090
- Adding concurrency and monitoring
- 4. 建置
-
- 1. 配置並啟動用於 Django 的 uWSGI 伺服器[?](https://docs.djangoproject.com/zh-hans/2.2/howto/deployment/wsgi/uwsgi/#configuring-and-starting-the-uwsgi-server-for-django)
- 2. virtual 的安裝使用
- 3. 靜態資源配置
- 5. 執行
- 6. 說明
- 7. 天坑
-
-
- 1. uwsgi解析靜態檔案不生效
-
1. 為什麼要用 uWSGI 建置
1. WSGI 協定, uwsgi 協定:
WSGI 協定(通訊協定):Python 用於 Web 開發的協定(用於處理 Web 伺服器和應用程式(APP)的互動訊息)。把HTTP通訊的過程抽象出來(請求資料,回應資料的分析),開發者只負責處理中途的資料。
uwsgi 協定(傳輸協定,速度很快):uWSGI 程式實現的一個自有的協定(採用二進位制來儲存資料,之前的協定都是使用字串,所以在儲存空間和解析速度上,都更快)。
注意:
WSGI 是一種通訊協定。
uwsgi 是一種線路協定而不是通訊協定,在此常用於 uWSGI 伺服器與其他網路伺服器的資料通訊。
Web 框架一般都自帶 wsgi 伺服器,但是效能不好,只做測試用途。(django–wsgrief)
2. wsgiref 模組(django 框架自帶)
是 python 提供的,用於測試和學習的簡單的 WSGI 伺服器模組。
這個模組預設監聽8000連接埠,把 HTTP 請求,根據WSGI協定,轉換application中的environ引數,然後呼叫application函式。
wsgiref 會把 application 函式提供的回應頭設定轉換為 HTTP 協定的回應頭,把 application的回傳(return)作為回應體,根據HTTP協定,生成回應,回傳給瀏覽器。
3. uWSGI伺服器
uWSGI 是一個 Web 伺服器, 它實現了 WSGI 協定、uwsgi協定、http協定等。
4. Django 本身提供了 runserver,為什麼不能用來建置
runserver 方法是除錯 Django 時經常用到的執行方式,它使用 Django 自帶的 WSGI Server 執行,主要在測試和開發中使用,並且 runserver 開啟的方式也是單行程。
uWSGI 是一個 Web伺服器,具有超快的效能,低記憶體佔用和多 app 管理等優點,並且搭配 Nginx 就是一個生產環境了,能夠將使用者的訪問請求與應用 app 隔離,實現真正的建置。相對來說,支援的併發量更高,方便管理多執行緒,發揮多核優勢,提升效能。
5. 是否使用 Nginx 來做負載均衡
Nginx 的特點:
- 安全(Nginx作為專業伺服器,暴露在公網相對比較安全)。
- 能更好的處理靜態資源(一些 http request header)。
- Nginx 也可以快取一些動態內容,可以更好的配合CDN。
- 可以進行多台機器的負載均衡。
當然,如果訪問量不大,uWSGI 足以勝任。
Nginx 和 uWSGI 伺服器之間如何配合工作?
-
首先瀏覽器發起 http 請求到 nginx 伺服器。
-
Nginx 根據接收到的請求包,進行 url 分析,判斷訪問的資源型別,如果是靜態資源,直接讀取靜態資源回傳給瀏覽器。
-
如果請求的是動態資源就轉交給 uwsgi 伺服器, uwsgi 伺服器根據自身的 uwsgi 和 WSGI 協定,找到對應的 Django 框架。
-
Django 框架下的應用進行邏輯處理後,將回傳值發送到 uwsgi 伺服器, 然後 uwsgi 伺服器在回傳給 nginx。
-
最後 nginx 將回傳值回傳給瀏覽器渲染顯示給使用者。
2. uWSGI 的安裝:
1. yum 安裝(最快捷的方式)
1 2 3 | # yum/apt-get install build-essential python-dev # Install current stable version. $ pip install uwsgi |
2. 原始碼安裝
1 2 | # Or install LTS (long term support). $ pip install https://projects.unbit.it/downloads/uwsgi-lts.tar.gz |
速查手冊: uWSGI
3. uWSGI 安裝不了
uwsgi安裝報錯:plugins/python/uwsgi_python.h:2:20: fatal error: Python.h: 沒有那個檔案或目錄
錯誤訊息:plugins/python/uwsgi_python.h:2:20: fatal error: Python.h: 沒有那個檔案或目錄
方案:pip install python3-devel 或者 pip install python-devel
安裝的分別對應python3和python2.7
網上都是這麼說的。可是我始終在 yum 中 找不到有效的 python3-devel。 雖然安裝了一個python34-devel,可安裝uwsgi還是會報錯。
接下來我嘗試了很多辦法:自己下載rpm包安裝、更新yum源,但是都沒有效果。
最終解決是從一台可以查到 python3-devel 的 伺服器上,拷貝出了 yum.repos.d 裡面的倉庫,複製在安裝不了的主機中。然後yum update一下,就可以安裝了,==。
3. uWSGI 快速教學
The first WSGI application
Let』s start with a simple 「Hello World」 example:
1 2 3 | def application(env, start_response): start_response('200 OK', [('Content-Type','text/html')]) return [b"Hello World"] |
(save it as
As you can see, it is composed of a single Python function. It is called 「application」 as this is the default function that the uWSGI Python loader will search for (but you can obviously customize it).
Deploy it on HTTP port 9090
Now start uWSGI to run an HTTP server/router passing requests to your WSGI application:
1 | uwsgi --http :9090 --wsgi-file foobar.py |
That』s all.
Do not use
--http when you have a frontend webserver or you are doing some form of benchmark, use--http-socket . Continue reading the quickstart to understand why.
Adding concurrency and monitoring
The first tuning you would like to make is adding concurrency (by default uWSGI starts with a single process and a single thread).
You can add more processes with the
1 | uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2 |
This will spawn 4 processes (each with 2 threads), a master process (will respawn your processes when they die) and the HTTP router (seen before).
One important task is monitoring. Understanding what is going on is vital in production deployment. The stats subsystem allows you to export uWSGI』s internal statistics as JSON:
1 | uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191 |
Make some request to your app and then telnet to the port 9191, you』ll get lots of fun information. You may want to use 「uwsgitop」 (just
Attention
Bind the stats socket to a private address (unless you know what you are doing), otherwise everyone could access it!
4. 建置
1. 配置並啟動用於 Django 的 uWSGI 伺服器?
uWSGI 支援多種配置行程的方式。參考 uWSGI 的 配置檔案。
以下是個範例命令,用於啟動一個 uWSGI 伺服器:
1 2 3 4 5 6 7 8 9 10 11 12 | uwsgi --chdir=/path/to/your/project --module=mysite.wsgi:application --env DJANGO_SETTINGS_MODULE=mysite.settings --master --pidfile=/tmp/project-master.pid --socket=127.0.0.1:49152 # can also be a file --processes=5 # number of worker processes --uid=1000 --gid=2000 # if root, uwsgi can drop privileges --harakiri=20 # respawn processes taking more than 20 seconds --max-requests=5000 # respawn processes after serving 5000 requests --vacuum # clear environment on exit --home=/path/to/virtual/env # optional path to a virtualenv --daemonize=/var/log/uwsgi/yourproject.log # background the process |
假設你有個叫做
Django 指定的引數如下:
chdir :需要包含於 Python 的匯入路徑的目錄的路徑——例如,包含mysite 包的目錄。module :要使用的 WSGI 模組——可能是startproject 建立的mysite.wsgi 的模組。env :至少要包括DJANGO_SETTINGS_MODULE 。home : 可選的路徑,指向你工程的 virtualenv。
範例 ini 設定檔:
1 2 3 4 5 6 7 8 | [uwsgi] chdir=/path/to/your/project module=mysite.wsgi:application master=True pidfile=/tmp/project-master.pid vacuum=True max-requests=5000 daemonize=/var/log/uwsgi/yourproject.log |
範例 ini 設定檔語法:
1 | uwsgi --ini uwsgi.ini |
為檔案上傳修復
如果上傳的檔名包含非 ASCII 字元時,可能丟擲
1 | env = LANG=en_US.UTF-8 |
參考 Unicode 參考指引的 Files 章節取得細節訊息。
參考 uWSGI 檔案 管理 uWSGI 行程 取得更多關於開啟,關閉和過載 uWSGI workers 的訊息。
2. virtual 的安裝使用
- 廖雪峰教學:virtualenv
- uwsgi+virtualenv關於python版本的設定問題完美解決辦法(未用到)
建立 virtual 環境
-
1
2$ pip3 install virtualenv
$ virtualenv venv
進入、退出環境
-
1
2$ source venv/bin/activate
(venv)$ deactivate
在 uwsgi.ini 中配置:
home : 可選的路徑,指向你工程的 virtualenv。
如果遇到無法找到包的情況:
- 在原有環境中 使用 import xxx; print(xxx.path_); 檢視包路徑。
- 使用 cp -r /源路徑/包名 venv/lib/pythonxxx/site-packages/ 將原有的包拷貝至新環境中。
3. 靜態資源配置
第一步: 收集靜態資源
1 | python manage.py collectstatic |
這一步的作用是將我們 Django 專案自帶的靜態檔案收集進專案目錄 /collectstatic 資料夾中,例如:admin站點的css、js檔案。
第二步:在 settings.py 檔案中配置
1 | STATIC_ROOT = os.path.join(BASE_DIR, 'collectstatic/') |
兩種啟動方式:
A、不帶靜態檔案啟動,靜態檔案將無法載入,頁面不正常顯示
–chdir /opt/mywebapp/ django專案根路徑
wsgi-file mysite/wsgi.py django專案settings.py所在目錄下的 wsgi.py檔案
1 | uwsgi --http 0.0.0.0:9000 --chdir /opt/mywebapp/ --wsgi-file mysite/wsgi.py --master --processes 4 --threads 2 |
B、帶靜態檔案啟動,也就是網頁開啟後,頁面能正常顯示
–chdir /opt/mywebapp/ django專案根路徑
wsgi-file mysite/wsgi.py django專案settings.py所在目錄下的 wsgi.py檔案
–static-map=/static=static django專案web頁面靜態檔案,所在根目錄的』static』目錄
–static-map=/static=media django專案內容靜態檔案,所在根目錄的』media』目錄
注意:這裡僅是指static、media目錄,根目錄下還有其他blog,account,templates目錄等,可能也有人會問,是不是都需要把這些目錄都要一一加入–static-map裡面,答案是不需要,因為這些都不是django application對應的「static」目錄(已在settins設定,並可以讓其他views索引到static目錄),如果使用-static-map=/static=templates,uwsgi將無法找到相關靜態檔案
1 | uwsgi --http 0.0.0.0:9000 --chdir /opt/mywebapp/ --wsgi-file mysite/wsgi.py --static-map=/static=static --master --processes 4 --threads 2 |
注意,對於這種啟動方式,動、靜態資源都可以訪問
5. 執行
將我們執行期的配置寫入檔案 uWSGI.ini 中,方便以後服務的啟動與控制。
參考配置1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | [uwsgi] ; 監聽的連接埠 http = :8000 ; 指定和nginx進行套接字通訊的方式:連接埠或檔案 ; socket = 127.0.0.1:8001 ; socket = /home/kzzf/project/OfferHelp/OfferHelp.sock ; 專案所在目錄,和manage.py同級 chdir = /home/kzzf/project/OfferHelp ; 虛擬環境所在目錄 home=/home/kzzf/env/OfferHelp-env PYTHONHOME = /home/kzzf/env/OfferHelp-env/bin/ ; 主應用中的wsgi檔案 wsgi-file = website-root/wsgi.py ; 代理靜態資源:路徑對映 static-map = /static=/home/project/collectstatic ; 啟動一個master行程,來管理其餘的子行程 master=True processes = 4 threads = 2 ; 儲存主行程的pid,用來控制uwsgi服務 pidfile=/tmp/uwsgi.pid ; 啟動專案 uwsgi uwsgi.ini ; uwsgi --stop/reload xxx.pid 停止/重啟uwsgi ; 設定後台執行,儲存日誌 daemonize=/tmp/log/uwsgi.log ; deamonize=1 ; 用來配置background執行 ; 設定每個工作行程處理請求的上限,達到上限時,將回收(重啟)該行程。可以預防記憶體溢位 max-requests=5000 ; 連接最大等待時間 harakiri = 20 # 服務停止時自動移除unix Socket和pid檔案 vacuum=true |
參考配置2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | # uwsig使用設定檔啟動 [uwsgi] # 專案所在的根目錄 chdir=/opt/mywebapp/ # 指定專案的application,區別於啟動命令--wsgi-filemysite/wsgi.py module=mysite.wsgi:application #the local unix socket file than commnuincate to Nginx # 指定sock的檔案路徑,這個sock檔案會在nginx的uwsgi_pass配置,用來nginx與uwsgi通訊 # 支援ip+port模式以及socket file模式 #socket=%(chdir)/uwsgi_conf/uwsgi.sock socket=127.0.0.1:9001 # 行程個數 processes = 8 # 每個行程worker數 workers=5 procname-prefix-spaced=mywebapp # uwsgi的行程名稱前置詞 py-autoreload=1 # py檔案修改,自動載入 # 指定IP連接埠,web訪問入口 http=0.0.0.0:9000 # 指定多個靜態檔案:static目錄和media目錄,也可以不用指定該靜態檔案,在nginx中配置靜態檔案目錄 # uwsgi有自己的配置語法,詳細可參考官網,無需寫絕對路徑,可以用迴圈、判斷等高階配置語法 for =static media static-map=/static=%(chdir)/%(_) endfor = # 啟動uwsgi的使用者名稱和使用者組 uid=root gid=root # 啟用主行程 master=true # 自動移除unix Socket和pid檔案當服務停止的時候 vacuum=true # 序列化接受的內容,如果可能的話 thunder-lock=true # 啟用執行緒 enable-threads=true # 設定一個超時,用於中斷那些超過伺服器請求上限的額外請求 harakiri=30 # 設定緩衝 post-buffering=4096 # 設定日誌目錄 daemonize=%(chdir)/uwsgi_conf/uwsgi.log # uWSGI行程號存放 pidfile=%(chdir)/uwsgi_conf/uwsgi.pid #monitor uwsgi status 透過該連接埠可以監控 uwsgi 的負載情況 # 支援ip+port模式以及socket file模式 # stats=%(chdir)/uwsgi_conf/uwsgi.status stats = 127.0.0.1:9001 |
執行:
1 | uwsgi uwsgi.ini |
停止:
1 | uwsgi --stop /tmp/uwsgi.pid(ini中輸出的) |
6. 說明
以上說明使用uWSGI配置啟動django專案成功執行,因uWSGI的設定檔里已加入靜態檔案static-map,因此在不需要nginx的配置下,也可以支撐服務(只是效能未到完美級別),此部分的流程如下圖所示:
[外鏈圖像轉存失敗,源站可能有防盜鏈機制,建議將圖像儲存下來直接上傳
下面將使用nginx配置靜態檔案請求,uwsgi只負責動態請求部分的請求,各司其職,以進一步壓榨服務效能。如果確定使用nginx代理django專案靜態檔案服務,那麼配置之前,先把uwsgi.ini裡面的–static-map=/static部分註解掉。
7. 天坑
1. uwsgi解析靜態檔案不生效
問題描述:
按照官方教學,收集靜態檔案,並在 uwsgi 中配置了 static-map 後,uwsgi還是無法代理靜態資源訪問,請求會到Django中,查遍所有的教學不見其解。最後發現原因如下:
問題敘述:
1 | static-map = /static= /home/project/collectstatic |
更正後:
1 | static-map = /static=/home/project/collectstatic |
不知道你看出來沒,就是因為配置中多了一個空白,導致代理出錯。希望有問題的人早點看到這個點。