nginx + (uwsgi / gunicorn) + django + dwebsocket 部署

nginx + uwsgi + django + dwebsocket (남용이 있음)

nginx를 역방향 프록시로 사용합니다. 구성은 다음과 같습니다.

server {
    
    
        listen 80;
        server_name 域名; 
        charset utf-8;
        
        location / {
    
    
                # uwsgi配置
                include uwsgi_params;
                uwsgi_pass unix:/home/CSJ_uwsgi/uwsgi_socket.sock;
        }
        # 处理websocket请求
        location ~ /viewer/chat_room {
    
    
                # uwsgi配置
                include uwsgi_params;
                uwsgi_pass unix:/home/CSJ_uwsgi/uwsgi_socket.sock;
                # 下面两行是重点
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
        }
}

uwsgi를 웹 서버로 사용하는 경우 구성은 다음과 같습니다.

#使用nginx连接时使用
#socket = 127.0.0.1:8000
#直接做web服务器使用
#http = :8000
#项目目录
chdir = /home/CSJ_Live
#项目中wsgi.py文件的目录,相对于项目目录
#wsgi-file = CSJ_Live/wsgi.py
#指定项目的application
module = CSJ_Live.wsgi
#指定启动的工作的进程数
processes = 5
#请求超时事件
#harakiri = 60
#最大请求次数,重启进程,防止内存泄漏
max-request = 5000
#用户组相关配置
#uid = root
#gid = root
#指定工作进程中的线程数
#threads = 2
#启动主进程
master = True
#保存启动之后主进程的pid
pidfile = /home/CSJ_uwsgi/uwsgi.pid
#设置uwsgi后台允许,uwgsi.log保存日志信息
daemonize = /home/CSJ_uwsgi/uwsgi.log
#停止时自动删除进程号文件
vacuum=true
#设置缓冲
post-buffering=65535
#不设置会导致上传大文件失败
buffer-size=65535
#加载项目配置(django + websocket时需要配置的信息)
DJANGO_SETTINGS_MODULE=CSJ_Live.settings
WEBSOCKET_FACTORY_CLASS="dwebsocket.backends.uwsgi.factory.uWsgiWebSocketFactory"
socket = /home/CSJ_uwsgi/uwsgi_socket.sock
#这里给664就可以
chmod-socket = 777
#开启异步
async = 30
ugreen = ''
http-timeout = 300

프런트 엔드 HTML 테스트 케이스

<script>
        var num = '';
        for (var i=0;i<15;i++){
    
    
            num += Math.floor(Math.random()*9+1)
        }
        if ("WebSocket" in window) {
    
    
            {
    
    #alert("您的浏览器支持 WebSocket!");#}
            // 打开一个 web socket
            {
    
    #ws = new WebSocket('ws://' + window.location.host + '/viewer/chat_room/' + $("#event_uri_key").text());#}
            ws = new WebSocket('ws://' + window.location.host + '/viewer/chat_room/' + num);
            ws.onopen = function () {
    
    
                // Web Socket 已连接上,使用 send() 方法发送数据
                alert("onopen!");
            };

            ws.onmessage = function (evt) {
    
    
                var received_msg = evt.data;
                $("#info").text(received_msg);
            };

            ws.onclose = function () {
    
    
                // 关闭 websocket
                alert("连接已关闭...");
            };
            function sendmsg() {
    
    
                var msg = $("#msg").val();
                ws.send(msg);
                alert('发送成功')
            }
        }
</script>

settings.py는 다음 구성을 추가합니다.

#uwsgi聊天室dwebsocket配置文件
WEBSOCKET_FACTORY_CLASS = "dwebsocket.backends.uwsgi.factory.uWsgiWebSocketFactory"

장고 테스트 케이스

conn_dict = {
    
    }
@accept_websocket
def chat_room(request, event_uri_key):
    if request.is_websocket():
        conn = request.websocket 
        global conn_dict
        conn_dict[event_uri_key] = conn
        while True:
            msg = conn.wait()
            if msg == None:
                conn_dict.pop(event_uri_key)
                return
            else:
                for k, v in conn_dict.items():
                    v.send(str(conn_dict))

nginx 재시작

nginx -t
nginx -s reload

uwsgi 시작

처음 사용하는 경우

uwsgi --ini /路径/uwsgi.ini #自己的ini配置文件路径

다시 시작 및 종료 (pidfile = /home/CSJ_uwsgi/uwsgi.pid를 설정하면 pid 파일이 생성됨)

uwsgi --reload /路径/uwsgi.pid # pid文件路径
uwsgi --stop  /路径/uwsgi.pid # pid文件路径

요약하자면 :

달성하기 위해 channels + redis를 사용하고 싶지 않기 때문에 구덩이를 밟습니다.이 방법은 websocket 통신을 실현하지만 uwsgi 다중 프로세스는 django를 시작합니다. 전역 사전을 사용하여 연결 소켓을 저장합니다. 공유 변수는 적합하지 않습니다. 프로세스 간의 변수는 서로 분리되어 있습니다. 하위 프로세스의 전역 변수는 상위 프로세스의 데이터의 완전한 사본이며 하위 프로세스의 전역 변수 수정은 다른 프로세스의 전역 변수에 영향을주지 않습니다. 전혀 프로세스. 결과적으로이 사전은 여러 프로세스에서 서로 독립적이며 메시지 전달을 실현할 수 없습니다. uwsgi의 프로세스 수정 = 1은 작동하지 않습니다. 이로 인해 차단이 발생합니다. 긴 연결이 끊어지기 전에 다른 요청이 차단됩니다.

여기에서 프로세스 간 통신을 사용할 수 있습니다. 실패하면 시도하지 않았거나 redis를 추가합니다 (채널만큼 좋지 않음 ...).

nginx + gunicorn + django + dwebsocket (성공적으로 시작하려면 gunicorn 코 루틴 모드 사용)

nginx 구성은 다음과 같습니다.

server {
    
    
        listen 80;
        server_name 域名; 
        charset utf-8;
        
        location / {
    
    
                # gunicorn配置
                proxy_pass http://127.0.0.1:8000;
        }
        # 处理websocket请求
        location ~ /viewer/chat_room {
    
    
                # gunicorn配置
                proxy_pass http://127.0.0.1:8000;
                proxy_http_version 1.1;
                proxy_redirect off;
                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 Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";

        }
}

gunicorn 구성은 다음과 같습니다.

manage.py와 동일한 디렉토리에 gunicorn 구성 파일을 만듭니다.

#gunicorn.py
import logging
import logging.handlers
from logging.handlers import WatchedFileHandler
import os
import multiprocessing

bind = '127.0.0.1:8000'  # 绑定ip和端口号
backlog = 512  # 监听队列
chdir = '/home/CSJ_Live'  # gunicorn要切换到的目的工作目录
worker_class = 'gevent'  # 使用gevent模式,还可以使用sync 模式,默认的是sync模式
#workers = multiprocessing.cpu_count() * 2 + 1  # 进程数(多进程数据不互通,默认workers为1,单进程模式)
loglevel = 'info'  # 日志级别,这个日志级别指的是错误日志的级别,而访问日志的级别无法设置
errorlog = '../logs/gunicorn.error.log'  #错误日志
accesslog = '../logs/gunicorn.access.log' #访问日志

worker_class = 'gevent'를 사용하려면 gevent 모듈을 설치해야합니다.

pip3 install wheel
pip3 install gevent

셸 스크립트 시작, nohup 시작 등으로 작성할 수있는 gunicorn을 시작하는 방법에는 여러 가지가 있습니다.
여기서는 nohup 시작 (프로젝트 디렉터리에서 시작)을 사용합니다. (기본 데몬 = Flase)이 시작은 터미널을 차지합니다.

nohup gunicorn -c gunicorn.py CSJ_Live.wsgi:application

gunicorn 구성 파일에 매개 변수를 추가 한 다음 위의 명령을 사용하여 백그라운드에서 시작합니다.

daemon = True # 后台运行

프로세스 번호는 ps 명령을 통해 볼 수 있습니다.

ps aux | grep gunicorn

지금까지 websocket을 구현할 수 있습니다. 사실 근본적인 문제는 전역 사전의 유지 관리에 있습니다. uwsgi 시작과 비교하여 gunicorn + gevnet 시작의 장점은 gevent 코 루틴 시작 (set workers = 1, 기본값은 1)입니다. IO 차단 동작이 없으며 uwsgi는 차단을 유발합니다.

작업자 수가 1로 설정되지 않은 경우 다중 프로세스 시작 방법은 uwsgi와 동일한 문제가 발생하고 전역 사전을 사용할 수 없게됩니다. 프로세스 간 통신을 사용하여 해결해보십시오.

프런트 엔드 HTML 테스트 케이스

웹 소켓은 오랜 시간 동안 메시지 상호 통신이 없어 타임 아웃 연결이 끊어 질 수 있기 때문에 하트 비트 메커니즘이 도입 될 수 있습니다. 일반적인 논리는 프런트 엔드가 정기적으로 핑을 보내고 장고는 수신 후 즉시 퐁으로 돌아가고 프런트 엔드 타이머는 다음과 같습니다. 초기화. 여기에서는 하트 비트 및 재연 결과 같은 기능을 캡슐화하는 타사 라이브러리 websocket-heartbeat-js를 사용하므로 하트 비트 메커니즘을 수동으로 구현할 필요가 없습니다.

<body>
<input type="text" id="msg">
<button id="send" onclick="sendmsg()">发送</button>
</body>
<script src="/static/websocket-heartbeat-js-master/dist/index.js"></script>
<script src="/static/js/jquery-3.2.1.min.js"></script>
<script>
        const options = {
    
    
            url: 'ws://' + window.location.host + '/viewer/chat_room/' + '房间号',  # 房间号根据后端逻辑定义
            pingTimeout: 30000,
            pongTimeout: 10000,
            reconnectTimeout: 2000,
            pingMsg: "heartbeat"
        };
        let websocketHeartbeatJs = new WebsocketHeartbeatJs(options);
        websocketHeartbeatJs.onopen = function () {
    
    
            console.log('连接成功');
        };
        websocketHeartbeatJs.onmessage = function (e) {
    
    
            console.log(`onmessage: ${
      
      e.data}`);
        };
        function sendmsg() {
    
    
            var msg = $("#msg").val();
            websocketHeartbeatJs.send(msg)
        }
</script>

사용 방법은 반복되지 않습니다. websocket-heartbeat-js를 참조 하세요.

urls.py 구성

from django.conf.urls import url,re_path
from . import views
urlpatterns = [
    url(r"^chat_room/(?P<event_uri_key>[a-zA-Z0-9]{15})",views.chat_room) # 正则匹配房间号
]

장고 테스트 케이스

#dwebsocket
from dwebsocket.decorators import accept_websocket
from collections import defaultdict
#保存所有接入的用户地址
#用event_uri_key来区分房间,房间号作为键,值为字典,用 微信名称加头像 标识每一个套接字放入字典{房间号1:{微信名称-头像url:套接字,微信名称-头像url2:套接字2}}
allconn = defaultdict(dict)
@accept_websocket
def chat_room(request, event_uri_key):
    if request.is_websocket():
        # 获取用户相关信息
        user = request.COOKIES.get('user')
        nickname = json.loads(user)['nickname']  # 用户昵称
        headimgurl = json.loads(user)['headimgurl']  # 用户头像
        conn = request.websocket  # 连接套接字
        global allconn
        allconn[event_uri_key][nickname + '-' + headimgurl] = conn
        room_dict = allconn[event_uri_key]
        while True:
            message = conn.wait()
            if message == None:
                del allconn[event_uri_key][nickname + '-' + headimgurl]
                return
            elif message == b'heartbeat':  # 心跳响应
                conn.send(b"heartbeat") # pong
            else: # 转发到房间内所有连接
                for k, v in room_dict.items():
                    msg = nickname + '-' + headimgurl + '-' + bytes.decode(message)
                    v.send(str.encode(msg))

요약하자면 :

개인적으로 dwebsocket에 대한 uwsgi의 지원이 조금 불만족 스럽다고 생각합니다 .Uwsgi의 단일 프로세스 사용은 차단을 유발하고 여러 프로세스간에 데이터를 전달할 수없고 전달할 수 없습니다. gunicorn의 코 루틴 모드를 사용하면 메시지 전달 문제를 더 잘 해결할 수 있습니다.

추천

출처blog.csdn.net/weixin_44784018/article/details/103872287