在開發環境,我們一般使用python起一個web服務即可訪問,但是對於生產環境來說,我們一般使用nginx+uWSGI的方式進行部署。
使用Nginx優點:
- 安全:不管什么請求都要經過代理服務器,這樣就避免了外部程序直接攻擊web服務器
- 負載均衡:根據請求情況和服務器負載情況,將請求分配給不同的web服務器,保證服務器性能
- 提高web服務器的IO性能:對於一些靜態文件,可以直接由反向代理處理,不經過web服務器
相關概念:
wsgi web應用程序之間的接口。它的作用就像是橋梁,連接在web服務器和web應用框架之間。
uwsgi 是一種傳輸協議,用於定義傳輸信息的類型。
uWSGI 是實現了uwsgi協議WSGI的web服務器。
1. 添加測試項目
from flask import Flask app = Flask(__name__) @app.route("/") def index(): return 'flask project' @app.route("/user/<int:uid>") def user_profile(uid): return f'user id:{uid}' if __name__ == '__main__': app.run()
本項目是基於虛擬環境進行搭建,具體安裝查看virtualenv相關博客
2.創建新的虛擬環境,並安裝uwsgi
mkvirtualenv flask_env
pip3 install uwsgi
3. 在本項目下創建uwsgi配置文件,添加內容如下:
[uwsgi]
module = app:app # 相當於命令下的-w 指定模塊(wsgi-file,callable 這兩個可以被注視掉) #http = 127.0.0.1:5000 socket = 127.0.0.1:5000 # 支持http+socket兩種方式,這里選用socket virtualenv = /root/python_env/flask_project # 如果使用虛擬環境,需要進行執行 chdir = /data/python_projects/flask_project # 指定項目路徑 #wsgi-file = app.py # 項目入口文件 #callable = app # flask應用對象 process = 2 # 進程數 threads = 2 # 每個進程開啟的線程數 buffer-size = 32768 master = true # 主進程 daemonize = %(chdir)/uwsgi/uwsgi.log # 指的后台啟動 日志輸出的地方 pidfile = %(chdir)/uwsgi/uwsgi.pid # 保存主進程的進程號
py-autoreload=1 #熱加載開啟
touch-logreopen = %(chdir)/uwsgi/touchforlogrotate # 日志分割監聽文件
stats = 127.0.0.1:9191 # 添加監聽 使用nc 127.0.0.1 9191 查看 (nc 安裝:yum -y install nmap-ncat)
thunder-lock = true #防止驚群 harakiri = 60 #超時時間(秒)
vacuum = true #自動移除unix Socket和pid文件當服務停止的時候
完整配置項:
socket : 地址和端口號,例如:socket = 127.0.0.1:50000 processes : 開啟的進程數量 workers : 開啟的進程數量,等同於processes(官網的說法是spawn the specified number of workers / processes) chdir : 指定運行目錄(chdir to specified directory before apps loading) wsgi-file : 載入wsgi-file(load .wsgi file) stats : 在指定的地址上,開啟狀態服務(enable the stats server on the specified address) threads : 運行線程。由於GIL的存在,我覺得這個真心沒啥用。(run each worker in prethreaded mode with the specified number of threads) master : 允許主進程存在(enable master process) daemonize : 使進程在后台運行,並將日志打到指定的日志文件或者udp服務器(daemonize uWSGI)。實際上最常用的,還是把運行記錄輸出到一個本地文件上。 log-maxsize :以固定的文件大小(單位KB),切割日志文件。 例如:log-maxsize = 50000000 就是50M一個日志文件。 pidfile : 指定pid文件的位置,記錄主進程的pid號。 vacuum : 當服務器退出的時候自動清理環境,刪除unix socket文件和pid文件(try to remove all of the generated file/sockets) disable-logging : 不記錄請求信息的日志。只記錄錯誤以及uWSGI內部消息到日志中。如果不開啟這項,那么你的日志中會大量出現這種記錄: [pid: 347|app: 0|req: 106/367] 117.116.122.172 () {52 vars in 961 bytes} [Thu Jul 7 19:20:56 2016] POST /post => generated 65 bytes in 6 msecs (HTTP/1.1 200) 2 headers in 88 bytes (1 switches on core 0) log-maxsize: 日志大小,當大於這個大小會進行切分 (Byte) log-truncate: 當啟動時切分日志
4.啟動服務
uwsgi --ini config.ini
5.配置nginx代理轉發
location / { include uwsgi_params; uwsgi_pass 127.0.0.1:5000; }
因為轉發之后的通信是使用的uwsgi://127.0.0.1:5000進行通信,所以uwsgi配置socket方式
如果使用http的方式進行通信的話:
location / {
proxy_pass http://127.0.0.1:5000;
}
其他命令:
# 重啟uwsgi服務 uwsgi --reload uwsgi.pid # 停止uwsgi服務 uwsgi --stop uwsgi.pid
命令下的 服務啟動:
$ uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app --manage-script-name 會把 SCRIPT_NAME 處理移向 uwsgi , 因為 uwsgi 會更智能一些。與 --mount 聯用可以把向 /yourapplication 發送的請求 重定向到 myapp:app 。如果應用可以在根級別訪問,那么可以使用單個 / 來代替 /yourapplication 。 myapp 指 flask 應用的文件名稱(不含擴展 名)或者提供 app 的模塊名稱。 app 在應用內部可被調用(通常是 app = Flask(__name__) )。 如果要把應用部署於一個虛擬環境,則還需要加上 --virtualenv /path/to/virtual/environment 。可能還需要根據項目所使用的 Python 版本相應地加上 --plugin python 或者 --plugin python3 。
添加日志分割:
uwsgi沒有提供按天的日志切割配置,只提供了一個log-maxsize配置,當文件達到多大的時候自動切分,對於查找歷史日志還是很不方便。
這里可以用mv+touch-logreopen參數,移動日志文件后,讓uwsgi重新打開日志記錄,不過需要配合sh+crontab
配置文件添加:
touch-logreopen = %d./touchforlogrotat #指定監聽文件,修改后重新打開日志
這里我們指定日志文件就在項目目錄下叫uwsgi.daemonize.log,監聽項目目錄下的touchforlogrotat文件,如果文件發生變化,就重新打開日志,我們可以先將當前的uwsgi.daemonize.log文件移動到別的地發,再touch一下touchforlogrotat,之前的文件便停止寫入,重新生成了一個叫uwsgi.daemonize.log文件。
在當前項目下新建一個touchforlogrotate.sh,並新建日志文件備份的日志,這里就放在項目中的logs文件夾
#!/bin/bash DIR=`echo $(cd "$(dirname "$0")"; pwd)` LOGDIR="${DIR}/logs" sourcelogpath="${DIR}/uwsgi.daemonize.log" touchfile="${DIR}/touchforlogrotate" DATE=`date -d "yesterday" +"%Y%m%d"` destlogpath="${LOGDIR}/uwsgi.daemonize.${DATE}.log" mv $sourcelogpath $destlogpath touch $touchfile
crontab定時調用
0 0 * * * sh /projectpath/touchforlogrotate.sh
注意點:
1.請務必把 app.run()
放在 if __name__ == '__main__':
內部或者放在單獨 的文件中,這樣可以保證它不會被調用。因為,每調用一次就會開啟一個本地 WSGI 服務器。當我們使用 uWSGI 部署應用時,不需要使用本地服務器。