nginx搭建基於python的web環境
前言:
在搭建開始前,我們先來梳理下web服務工作流程,先看下圖:
1、使用者(PC)向web伺服器發起http請求
2、web伺服器判斷使用者請求檔案是否為靜態檔案,是則直接讀取靜態檔案並返回給使用者,不是則通過WSGI協議將請求丟給web框架(django)程式碼處理
3、看web框架是否啟動django中介軟體,如果啟用,則依據中介軟體對請求進行修改,如果不啟用,則進入下一步
4、web框架中的路由程式將根據請求中的url檔名將請求路由至相應py檔案
5、相應py檔案收到請求後根據使用者提交的引數進行計算(期間可能會呼叫資料庫),然後返回計算後的結果和自定義頭部資訊以及狀態碼返回
6、web框架將返回的資料打上通用識別符號(頭部資訊)後返回給web伺服器
7、web伺服器打上web伺服器的通用識別符號(頭部資訊)後返回給使用者
8、使用者收到返回的資料
通過上面可以看到django框架基於WSGI協議和web伺服器進行互動,那麼WSGI協議又是什麼呢? 咱們用程式碼來說明(虛擬碼。寫一個簡易的遵循WSGI協議的web伺服器軟體和django程式):
WSGI伺服器的程式:
class WSGI_WEB(object): def __init__(self): # 1. 建立套接字 self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 繫結 self.tcp_server_socket.bind(("", 7890)) # 3. 變為監聽套接字 self.tcp_server_socket.listen(128) def set_response_header(self, status, headers): self.status = status self.headers = [("server", "WSGI_simple_web v1.0")] self.headers += headers def run(self): new_socket, client_addr = self.tcp_server_socket.accept() env = new_socket.recv(1024) body = application(env, set_response_header) # env是web伺服器接收到瀏覽器傳送來的資料包;set_response_header為web伺服器的一個方法地址,目的是讓django幫web伺服器生成http頭部(不是以return的形式給web伺服器);此外還有這裡呼叫django裡的應用還有一個最核心的任務,就是獲取返回資料的body! header = self.status + self.headers response = header + body new_socket.send(response.encode("utf-8"))
django的app程式:
def application(env, start_response): start_response('200 OK', [('Content-Type','text/html')]) return [b"Hello World"]
問題:
在生產環境中使用django提供的簡易web伺服器效能太差,一般只用於除錯。強大的nginx又不支援WSGI,那麼怎麼辦呢?
方案:
在nginx和python應用之間加一層支援WSGI協議的web伺服器。以後靜態檔案由nginx進行處理,動態檔案丟給WSGI伺服器,然後WSGI伺服器再丟給web框架處理。最理想的支援WSGI協議的web伺服器就是uWSGI。
下面來詳細介紹下搭建uWSGI伺服器以及與nginx聯動的方法:
1、安裝uWSGI(支援WSGI的WEB伺服器):
centos下python3.6安裝uWSGI方法:
yum install -y gcc* pcre-devel openssl-devel python36-devel.x86_64 pip3.6 install uwsgi
2、開啟uWSGI服務
方式一:
uwsgi --http 192.168.31.123:80 --file teacher/wsgi.py --static-map=/static=static --http 監聽IP埠 --file 專案wsgi.py檔案路徑 --static-map 靜態檔案路徑
注意:執行這條命令的時候:一定要在這個專案目錄中~
方式二(使用配置檔案):
vi uwsgi.ini: [uwsgi] # 監聽埠(nginx採用反向代理模式時必填) http = 0.0.0.0:8888
# 專案目錄 chdir=/opt/test/test1/
# 啟動uwsgi的使用者名稱和使用者組 uid=root gid=root # 指定專案的application(我猜是這裡的“test1.wsgi”拼接上面的專案目錄後,就將專案中的wsgi.py檔案和uWSGI伺服器關聯起來了) module=test1.wsgi:application # 指定sock的檔案路徑(nginx採用本地模式時必填) socket=/opt/test/script/uwsgi.sock # 啟用主程序 master=true # 程序個數 workers=5 pidfile=/opt/test/script/uwsgi.pid # 自動移除unix Socket和pid檔案當服務停止的時候 vacuum=true # 序列化接受的內容,如果可能的話 thunder-lock=true # 啟用執行緒 enable-threads=true # 設定自中斷時間 harakiri=30 # 設定緩衝 post-buffering=4096 # 設定日誌目錄 daemonize=/opt/test/script/uwsgi.log # 設定隔多久載入一次專案程式碼 py-autoreload=1 執行配置檔案(注意:這裡用什麼賬戶執行的,以後滲透進來獲取到的就是什麼賬戶。所以這一步切忌不要用root執行。): uwsgi --ini uwsgi.ini
彩蛋:
重啟uWSGI程序: uwsgi --reload uwsgi.pid # 程式碼做變更後uWSGI程序不會立即載入,此時可以重啟一下uWSGI程序讓它生效。。。是不是感覺有點坑,沒事,可以在配置檔案中設定py-autoreload 關閉uWSGI程序: uwsgi --stop uwsgi.pid
3、配置nginx
方式一(反向代理模式):
upstream uwsgi{ server 10.10.10.29:8888; } server { listen 80; server_name localhost; #charset koi8-r; #access_log /var/log/nginx/host.access.log main; location / { proxy_pass http://uwsgi; # 通過反向代理和uWSGI伺服器關聯 } }
方式二(本地模式):
server { listen 8080; server_name localhost; #charset koi8-r; #access_log /var/log/nginx/host.access.log main; location / { include uwsgi_params; # 指定nginx和uWSGI伺服器的通訊方式 uwsgi_connect_timeout 30; uwsgi_pass unix:/opt/test/script/uwsgi.sock; # 通過sock檔案和uWSGI伺服器關聯! 因為nginx會去讀取.sock檔案,所以需要關閉selinux才行!!! } }
4、此時訪問django的admin管理後臺時,靜態資源會調取失敗。這時可以將該專案所有靜態資源統一收集到一個資料夾下,然後由nginx統一去調取,真正做到動靜分離(動的給uWSGI,靜的由nginx直接調取):
在settings.py中加入:
TATIC_ROOT = os.path.join(BASE_DIR, 'static_file')
執行如下命令(蒐集專案中所有靜態檔案至'static_file'目錄):
python3.6 manage.py collectstatic --noinput
此時會在專案目錄下生成一個'static_file'資料夾,內含admin和所有app涉及的靜態檔案。
在nginx中配置靜態檔案路徑(如果nginx和uWSGI不屬同一臺伺服器可以使用反向代理的方式來調取靜態檔案):
location /static/ { alias /opt/test/test1/static_file/; }
此時就可以訪問基於python後臺的web網