nginx,uwsgi,supervisor,channels,daphne部署django項目

最近做了一個項目,需要後端主動與前端進行通信,官方推薦使用的是channels(channels官方文檔). 把我的使用經歷以及部署過程,記錄一下,這篇文章主要記錄使用 uwsgi, nginx, supervisor,daphne部署過程,有時間的會把我用docker 部署的經歷也寫一下.

安裝channels,使用websocket
  1. 下載channels pip install channels
  2. 項目中配置channels ,在settings文件的INSTALLED_APPS加入channels
    在這裏插入圖片描述
  3. 在項目中新建routing.py文件,用來生成websocket的路由,這裏我的文件和官方的不太一樣,但是最終的效果是一樣的,就像我們平時寫urls.py的路由一樣,一個在應用目錄下,一個在項目下.我把routing.py建在應用目錄下.
    (1) 這是我的項目的目錄結構,以及我的routing.py文件的位置
    在這裏插入圖片描述
    (2) 這是routing.py文件的內容,具體寫什麼,先空着,一步一步來完善
from django.urls import path
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from struction.consumers import SendConsumer

application = ProtocolTypeRouter({
    # WebSocket chat handler
    "websocket": AuthMiddlewareStack(
        URLRouter([
        
        ])
    ),
})

(3) routing.py文件創建完成之後,在settings.py進行配置

#websocket
ASGI_APPLICATION = "struction.routing.application"  # 上面新建的 asgi 應用
  1. 然後在struction應用下新建一個consumers.py文件用來寫websocket通信的業務邏輯
    官方文檔中的案例是做一個聊天室,我實際的項目需求只需要給前端發消息,不需要做到官方文檔中那樣,所以在websocket的通信這部分做了修改.
import json
import time

from channels.generic.websocket import WebsocketConsumer


class SendConsumer(WebsocketConsumer):

    def connect(self):
        self.accept()

  #     # 斷開連接
    def disconnect(self, close_code):
      # Leave room group
        pass

  #     # 接受消息
    def receive(self, text_data=None, bytes_data=None):
    	# 和前端通信接收前端的消息
        text_data_json = json.loads(text_data)
        message = text_data_json['message']
        print(text_data_json, 'websocket-----------')

        # 這個代碼測試用,給前端發消息,我這裏的是測試的demo,前端給我發什麼,我回什麼
        self.send(text_data=json.dumps({
            # 'message': json.dumps(msg)
            'time': time.time(),
            'message': message
        }))
  1. 完成之後,在routing.py配置路由
from django.urls import path
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from struction.consumers import SendConsumer

application = ProtocolTypeRouter({
    # WebSocket chat handler
    "websocket": AuthMiddlewareStack(
        URLRouter([
         	# 這個就是前端與websocket通信時訪問的路由
            path("ws/channel/websocket/", SendConsumer),
        ])
    ),
})

這個位置官方有一個註釋,爲了區分websocket通信和普通的http通信,最好在路由前面加上一個/ws/的前綴,這是爲了方便在生產環境部署.
特別是對於大型站點,可以配置生產級HTTP服務器(如nginx)以基於以下路徑路由請求:
(1)生產級WSGI服務器(如Gunicorn+Django,用於普通HTTP請求),
(2)生產級ASGI服務器(如Daphne+Channels,用於WebSocket請求)。
也就是說我們的Django 項目和 websocket都需要部署.這個後面部署的時候我會再寫.

  1. 以上內容完成之後,我自己寫了個簡單的測試頁面
    在這裏插入圖片描述
    room.html的內容如下
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br/>
<input id="chat-message-input" type="text" size="100"/><br/>
<input id="chat-message-submit" type="button" value="Send"/>
</body>
<script>

    console.log('------')
    var chatSocket = new WebSocket(
        <!--這個是前端和後端請求連接的地址,這裏的ip 端口,換成你自己的即可,這裏的端口號,要寫你用nginx代理之後的-->
        <!--你如果是本地測試,就寫你啓動項目用的端口就可以,我本地啓動的時候用的8008端口 -->
        <!-- python manage.py runserver 0.0.0.0:8008 -->
        'ws://' + '192.168.1.187:8008' +
        '/ws/channel/websocket/');
	
    document.querySelector('#chat-message-submit').onclick = function (e) {
        var messageInputDom = document.querySelector('#chat-message-input');
        var message = messageInputDom.value;
        <!--前端連接之後發送消息 -->
        chatSocket.send(JSON.stringify({
            'message': message
        }));

        messageInputDom.value = '';
    };

    chatSocket.onmessage = function (e) {
        console.log(e);
        var data = JSON.parse(e.data);
        var message = data['message'];
        document.querySelector('#chat-log').value += (message + '\n');
        console.log(message);
    };

    chatSocket.onclose = function (e) {
        console.error('Chat socket closed unexpectedly');
    };
</script>
</html>
  1. 頁面寫好之後寫一個view視圖,測試用
    index 函數寫在 struction.views.py 文件中
    在這裏插入圖片描述
    urls.py中要導入index from struction.views import index
    在這裏插入圖片描述
  2. 測試的時候,在瀏覽器訪問 192.168.1.187:8008/websocket/ ,效果如下
    在這裏插入圖片描述
    輸入值測試一下效果.
    這樣websocket在本地就能正常運行了,但是我們的項目開發完之後,還要部署到生產服務器,如果用nginx,uwsgi來部署項目的話,websocket也需要部署才能正常使用,下面我就記錄一下我的部署過程.
服務器使用nginx,uwsgi,daphne,supervisor部署項目
  1. 安裝uwsgi
    pip install uwsgi
    安裝完成之後,在項目目錄下建立一個uwsgi,放置uwsgi.ini的配置文件,以及日誌等信息
-- Video
	-- struction
	-- uwsgi
		-- uwsgi.ini
	-- static
	-- template
	-- Video
	...
	...

uwsgi.ini配置文件

uwsgi.ini
# 文件裏的這個[uwsgi]必須要寫,否則會報錯的 
# Can't find section "uwsgi" in INI configuration file myweb_uwsgi.ini
[uwsgi]
#http =:8090 # 如果不使用nginx 代理,直接運行就配http
socket = 192.168.1.187:8080 # 與nginx 通信的地址和端口,ip就寫你自己服務器的地址,port是指定項目運行時的端口號
chdir = /data/videostruction/Video # 項目目錄
home = /data/videostruction/venv # 加載虛擬環境的目錄
module = Video.wsgi # 改成自己的項目
master = true
workers = 5
processes = 2
threads = 2
vacuum = true
pidfile = %(chdir)/uwsgi/uwsgi.pid
daemonize = %(chdir)/uwsgi/uwsgi.log
#async = 30
ugreen = ''

配置完之後可以先不啓動,用supervisor來統一管理.supervisor是一個進程管理工具,可以很方便的監聽,啓動,停止進程,我們的項目要用到nginx,uwsgi,daphne 都可以用supervisor來管理,方便省事.

  1. 安裝supervisor,部署daphne
    (1) 在項目的wsgi.py文件目錄下,添加asgi.py 文件,配置信息如下
asgi.py
import os
import django
#import channels.asgi
from channels.routing import get_default_application

#  這裏的Video別忘了改成你自己的項目
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Video.settings")    
django.setup()
application = get_default_application()

(2)安裝supervisor, apt-get install supervisor如果有權限要求,在前面加上sudo 即可.安裝成功之後,生成supervisor的配置文件,echo_supervisord_conf > /etc/supervisord.conf,在配置文件中,添加daphne的配置信息.

supervisor.conf
# 我只把需要添加的部分寫在這裏,原文件的其他配置不需要改動
# 配置daphne有兩種方式,我都寫在了下面,第一種是官方的寫法,第二種是百度到的其他的配置方式
# 兩種用一種即可,我把兩種都記錄下來了
# 第一種
[fcgi-program:asgi]
# nginx 代理時要用到的
socket=tcp://0.0.0.0:8001
directory=/data/videostruction/Video  # 這裏的目錄是你下面執行command命令時所在目錄,如果這個地方不對,下面的目錄也就執行不了
# 這裏注意,如果你的服務器中沒有 /run/daphne/的目錄,要自己手動建一個,否則會報錯,提示找不到目錄
command=daphne -u /run/daphne/daphne%(process_num)d.sock --fd 0 --access-log - --proxy-headers Video.asgi:application
numprocs=4
process_name=asgi%(process_num)d
# 自動啓動
autostart=true
# 自動重啓
autorestart=true
# 這裏是日誌的輸出位置,可以自定義,自定義完要保證目錄存在
stdout_logfile=/tmp/asgi.log
redirect_stderr=true

# 第二種
[program:daphne]
socket=tcp://0.0.0.0:8001
directory=/data/videostruction/Video
# 啓動時指定ip和端口, Video不要忘記改
command=daphne -b 0.0.0.0 -p 8001 --proxy-headers Video.asgi:application
numprocs=4
process_name=asgi%(process_num)d
autostart=true
autorestart=true
stdout_logfile=/tmp/asgi.log
redirect_stderr=true

[program:uwsgi]
command=uwsgi --ini uwsgi/uwsgi.ini
# 這裏的目錄含義同上
directory=/data/videostruction/Video
autostart=true
autorestart=true
stdout_logfile=/data/videostruction/Video/uwsgi/uwsgi_out.log
stderr_logfile=/data/videostruction/Video/uwsgi/uwsgi_err.log

修改完配置文件之後需要更新一下
sudo supervisorctl reread
sudo supervisorctl update
(3) 以上內容配置完成之後,來安裝nginx,配置nginx 的代理信息,安裝nginx的過程我就不寫了,我直接寫配置過程,安裝教程我可以推薦一個.我安裝的時候就是參照的這個教程,配置configure時,用的是./configure --prefix=/usr/local/nginx這種方式.
ngixn教程:https://blog.csdn.net/u012453843/article/details/69396434
這位博主寫了很多的博客,都很不錯的博客,我還參照過他的博客,搭建了fastdfs服務器,寫的也是很詳細.
接下來繼續nginx的配置,把下面的內容添加到nginx.conf中,

# 這是配置daphne 的,可以單獨佔用一個端口,也可以和django項目共用一個,我這裏是和項目共用一個
upstream wsbackend {
		# 這裏的端口是你在supervisor中啓動daphne時指定的端口
        server 192.168.1.187:8001; 
    }
        
    server {
      listen       8090; # 監聽的端口(服務器要開放此端口,用戶可以通過這個端口訪問到項目,如果有請求到這個端口,就轉發到項目
      server_name  localhost;
      charset      UTF-8;
      client_max_body_size 600M;  # disable any limits to avoid HTTP 413 for large image uploads
      #  轉發websocket的請求,請求地址如果以/ws/channel/開頭就轉發到websocket,
      # 這裏就是之前官方文檔解釋的,爲了區別普通http請求和ws的請求
      location /ws/channel {
          proxy_pass http://wsbackend;
          proxy_redirect off;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection "upgrade";
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Host $server_name;
      }

      location / {
          include uwsgi_params;
          # 這裏的配置信息要和uwsgi.ini的配置信息一致,不然請求可能會轉發不過去
          uwsgi_pass 192.168.1.187:8080;
          uwsgi_read_timeout 20;
      }
      # 媒體文件的目錄
      location /media {
          alias /data/videostruction/Video/media;
      }
	  # 靜態文件的目錄
      location /static {
          #expires 30d;
          #autoindex on;
          #add_header Cache-Control private;
          alias /data/videostruction/Video/static;
      }
    }

配置完成之後就可以啓動supervisor和nginx,nginx的啓動也可以用supervisor來管理,我寫的時候沒有用,如果也想用也可以在supervisor.conf中加上,

[program:nginx]
command=/usr/local/nginx/sbin/nginx
autostart=true
autorestart=true
; 如果/var/log/nginx不存在,要手動創建
stdout_logfile=/var/log/nginx/nginx_out.log
stderr_logfile=/var/log/nginx/nginx_err.log

啓動supervisor supervisord -c /etc/supervisord.conf.  
我沒有用supervisor管理nginx,還需要單獨啓動nginx /usr/local/nginx/sbin/nginx.  
如果啓動不了,還可以用 /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

如果使用nginx代理,測試頁面room.html中的端口和ip不要忘了改.

參考博客: channels實現websocket實時通訊和消息推送https://blog.csdn.net/Wb199812/article/details/100087715
參考博客:django+uwsgi+daphne+supervisor生產環境部署https://www.cnblogs.com/wdliu/p/10032180.html

發佈了8 篇原創文章 · 獲贊 4 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章