前言:html
在搭建開始前,咱們先來梳理下web服務工做流程,先看下圖:python
一、用戶(PC)向web服務器發起http請求linux
二、web服務器判斷用戶請求文件是否爲靜態文件,是則直接讀取靜態文件並返回給用戶,不是則經過WSGI協議將請求丟給web框架(django)代碼處理nginx
三、看web框架是否啓動django中間件,若是啓用,則依據中間件對請求進行修改,若是不啓用,則進入下一步web
四、web框架中的路由程序將根據請求中的url文件名將請求路由至相應py文件數據庫
五、相應py文件收到請求後根據用戶提交的參數進行計算(期間可能會調用數據庫),而後返回計算後的結果和自定義頭部信息以及狀態碼返回django
六、web框架將返回的數據打上通用標識符(頭部信息)後返回給web服務器centos
七、web服務器打上web服務器的通用標識符(頭部信息)後返回給用戶瀏覽器
八、用戶收到返回的數據服務器
經過上面能夠看到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聯動的方法:
一、安裝uWSGI(支持WSGI的WEB服務器):
centos下python3.6安裝uWSGI方法:
yum install -y gcc* pcre-devel openssl-devel python36-devel.x86_64
pip3.6 install uwsgi
二、開啓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
三、配置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才行!!! } }
四、此時訪問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網站了