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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章