Websocket 连接在生产环境中失败。 (Nginx、Gunicorn、Daphne)(Django/React)

问题描述 投票:0回答:0

我正在尝试将我的 Django Rest Framework 应用程序部署到生产环境中。我有自己的运行 Debian 的服务器。我对部署 DRF 和 React 应用程序并不陌生,应用程序的 WSGI 部分与 Gunicorn 配合得很好。我无法解决的问题是无论我做什么我都无法从 Django Channels 连接到我的 Websocket。

有关更多信息,运行

python manage.py runserver
并在本地运行一切正常,我通常连接到我的 websocket。

我的routing.py文件:

from channels.routing import ProtocolTypeRouter, URLRouter
from django.urls import path, re_path

from apps.chat_app.consumers import ChatConsumer

websocket_urlpatterns = [
    path('ws/chat/<int:id>/<int:curr>/', ChatConsumer.as_asgi()),
]

application = ProtocolTypeRouter({
    'websocket':
        URLRouter(
            websocket_urlpatterns
        )
    ,
})

我的消费者档案:

import json

from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncWebsocketConsumer
from django.contrib.auth import get_user_model

from apps.chat_app.models import Message


class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        current_user_id = self.scope['url_route']['kwargs']['curr']
        other_user_id = self.scope['url_route']['kwargs']['id']

        self.room_name = (
            f'{current_user_id}_{other_user_id}'
            if int(current_user_id) > int(other_user_id)
            else f'{other_user_id}_{current_user_id}'
        )

        self.room_group_name = f'chat_{self.room_name}'

        await self.channel_layer.group_add(self.room_group_name, self.channel_name)
        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(self.room_group_name, self.channel_layer)
        await self.disconnect(close_code)

    async def receive(self, text_data=None, bytes_data=None):
        data = json.loads(text_data)
        message = data.get('message', '')
        sender_username = data['sender'].replace('"', '')
        sender = await self.get_user(username=sender_username)

        typing = data.get('typing', False)
        delete = data.get('delete', '')

        if typing:
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'user_typing',
                    'sender': sender_username,
                    'msg': f'{sender.first_name.capitalize()} {sender.last_name.capitalize()} is typing...',
                }
            )
        elif delete:
            await self.delete_message(msg_id=data['delete'])

            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'message_delete',
                    'msg_id': data['delete'],
                }
            )
        else:
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'user_typing',
                    'sender': sender_username,
                    'msg': '',
                }
            )

            if message:
                msg = await self.save_message(sender=sender, message=message, thread_name=self.room_group_name)

                await self.channel_layer.group_send(
                    self.room_group_name,
                    {
                        'type': 'chat_message',
                        'msg_id': msg.id,
                        'message': message,
                        'sender': sender_username,
                        'timestamp': msg.timestamp.strftime('%d/%m/%Y %H:%M'),
                        'full_name': f'{sender.first_name.capitalize()} {sender.last_name.capitalize()}',
                    },
                )

    async def message_delete(self, event):
        msg_id = event['msg_id']

        await self.send(
            text_data=json.dumps(
                {
                    'delete': msg_id,
                }
            )
        )

    async def user_typing(self, event):
        username = event['sender']
        msg = event['msg']

        await self.send(
            text_data=json.dumps(
                {
                    'is_typing': True,
                    'sender': username,
                    'msg': msg,
                }
            )
        )

    async def chat_message(self, event):
        message = event['message']
        username = event['sender']
        full_name = event['full_name']
        msg_id = event['msg_id']
        timestamp = event['timestamp']
        typing = event.get('typing', False)
        delete = event.get('delete', '')

        if typing:
            await self.send(
                text_data=json.dumps(
                    {
                        'sender': username,
                        'typing': typing,
                    }
                )
            )
        elif delete:
            await self.send(
                text_data=json.dumps(
                    {
                        'delete': delete,
                    }
                )
            )
        else:
            if message:
                await self.send(
                    text_data=json.dumps(
                        {
                            'msg_id': msg_id,
                            'message': message,
                            'timestamp': timestamp,
                            'sender': username,
                            'full_name': full_name,
                        }
                    )
                )

    @database_sync_to_async
    def get_user(self, username):
        return get_user_model().objects.filter(username=username).first()

    @database_sync_to_async
    def save_message(self, sender, message, thread_name):
        return Message.objects.create(sender=sender, message=message, thread_name=thread_name)

    @database_sync_to_async
    def delete_message(self, msg_id):
        Message.objects.filter(id=msg_id).delete()

我的 asgi.py 文件:

import os
from django.core.asgi import get_asgi_application

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'inp_proj.settings')
django_asgi_app = get_asgi_application()

import apps.chat_app.routing

application = ProtocolTypeRouter(
    {
        'http': django_asgi_app,
        'websocket': AllowedHostsOriginValidator(
            AuthMiddlewareStack(URLRouter(apps.chat_app.routing.websocket_urlpatterns))),
    }
)

我的daphne.service文件:

[Unit]
Description=WebSocket Daphne Service
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/www/projectdir
ExecStart=/www/projectdir/venv/bin/python /www/projectdir/venv/bin/daphne -b 0.0.0.0 -p 8001 proj.asgi:application
Restart=on-failure

[Install]
WantedBy=multi-user.target

我的 gunicorn.service 文件:

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=jan
Group=www-data
WorkingDirectory=/www/projectdir
ExecStart=/www/projectdir/venv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          proj.wsgi:application

[Install]
WantedBy=multi-user.target

我的 gunicorn.socket 文件:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

最后,我的 nginx 配置文件:

upstream websocket {
    server 127.0.0.1:8001;
}

server {
    server_name 127.0.0.1 mydomain;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /www/projdir;
    }
    
     location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
    
    location /ws/ {
    proxy_pass http://websocket;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        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 X-Forwarded-Host $server_name;    
    }
}

所有服务(gunicorn socket、gunicorn service、daphne、nginx)都正常运行。 gunicorn WSGI 部分工作正常,整个应用程序正常工作,一切正常,除了我无法连接到我的 websocket。这就是我在客户端代码中连接到 websocket 的方式:

    const client = useMemo(() => {
        return new w3cwebsocket(`ws://mydomain:8001/ws/chat/${id}/${userId}/`);
    }, [id, userId]);

此外,我尝试输入 [serveripv4address]:8001 而不是 mydomain:8001,我在没有端口 8001 的情况下尝试了它,我尝试了 wss 和 ws,即使它是 HTTP。同样在我允许的主机中,我允许域甚至服务器 ipv4 地址。

我几乎尝试了所有我能想到的和我看到的每一个帖子。我的 Nginx、gunicorn 或 Daphne 没有显示任何错误。

django nginx websocket django-channels daphne
© www.soinside.com 2019 - 2024. All rights reserved.