如何在 Pythonanywhere 生产环境中 运行 WebSocket (django-channels)?

How to run WebSocket (django-channels) in production Pythonanywhere?

在学习了官方 Django-channels 教程后,我使用 WebSockets 创建了一个简单的聊天应用程序。但是,我无法让它在生产中工作。我做了一些 google 搜索,在 Pythonanywhere 中找到一个论坛,说他们不支持 WebSocket,我联系了团队,他们告诉我同样的事情。 我做了更多 google 搜索,发现了与 Daphne 服务器、Nginx 和其他一些我以前从未听说过的东西相关的东西。

由于我是 Django 频道的新手,所以我现在很困惑!我可以做些什么来使我的 WebSocket 网站 运行 在 Pythonanywhere 中正常生产(当然是免费的)或者我必须删除所有 WebSocket 代码并将其替换为重复的 Http 调用以检查新消息(用 AJAX)?

如果没有其他解决方案,只能转向重复的 Http 调用,是否有任何其他网络托管服务提供免费播放,包括免费 SSL 认证、域名(例如 mydomain.servicename.com)随机字符和 WebSocket 支持?

谢谢

我使用的代码

我不知道它是否相关,而且它在开发中也很完美,所以我认为它没有错误

settings.py:

INSTALLED_APPS = [
    'channels',
    ...
    'django_cleanup',
]

ASGI_APPLICATION = 'orgachat.routing.application'

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}

main routing.py(在路由文件夹中)

from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from chat.routing import websocket_urlpatterns as chat_routing

application = ProtocolTypeRouter({
    "websocket": AuthMiddlewareStack(
        URLRouter(
            chat_routing,
        )
    )
})

routing.py 用于聊天应用

from django.urls import path
from . import consumers

websocket_urlpatterns = [
    path('ws/chat/room/<int:room_id>/', consumers.RoomConsumer),
]

consumers.py

import json
from channels.generic.websocket import AsyncWebsocketConsumer


class RoomConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.group_name = str(self.scope['url_route']['kwargs']['room_id'])
        await self.channel_layer.group_add(self.group_name, self.channel_name)
        await self.accept()

    async def disconnect(self, code):
        await self.channel_layer.group_discard(self.group_name, self.channel_layer)

    async def receive(self, text_data):
        message_json = json.loads(text_data)
        await self.channel_layer.group_send(self.group_name, {
            'type': 'send_message',
            'content': message_json['content'],
            'area': message_json['area'],
            'area_id': message_json['area_id'],
            'username': self.scope['user'].username,

        })

    async def send_message(self, event):
        await self.send(json.dumps(event))

完整的 js 脚本

<script>
        // -------------------
        // WEBSOCKET SETUP
        // -------------------
        var wsStart = 'ws://'
        var hostName = window.location.hostname + ':8000'
        if (window.location.protocol.includes('https')) {
            wsStart = 'wss://'
            hostName = window.location.hostname
        };
        let endpoint = wsStart + hostName + '/ws' + window.location.pathname
        console.log(endpoint)
        var socket = new WebSocket(endpoint);

        socket.onmessage = function (e) {
            // todo not show message if in a different room
            data = JSON.parse(e.data);
            console.log(data.area_id)
            console.log(data.area)
            var sender = 'other'
            var username = data.username
            if (data.username == "{{ user.username }}") {
                sender = 'self';
                username = 'You'
            }
            document.querySelector('.messages').innerHTML += `
            <div class="message ${sender}">
            <p>${username} &mdash; ${data.area}:</p>
            <p>${data.content}</p>
        </div>
            `
            document.querySelector('#notification_sound').play()
        }
        socket.onerror = function (e) {
            alert("SERVER ERROR 500, You won't be able to see messages unless you refresh,")
        }
        socket.onclose = function (e) {}

        document.addEventListener('DOMContentLoaded', function () {
            document.querySelector('#sendMessage').onclick = function (e) {
                e.preventDefault();
                // ------------AJAX: SEND AND MESSAGE---------------
                let xhr = new XMLHttpRequest();
                xhr.onreadystatechange = function (e) {
                    if (this.readyState == 4 && this.status == 200) {
                        document.querySelector('#id_content').value = '';
                    }
                }
                xhr.open("POST", "{% url 'chat:room' room.id %}", true);
                xhr.setRequestHeader('Content-type', "application/x-www-form-urlencoded");
                data = {
                    'csrfmiddlewaretoken': '{{ csrf_token }}',
                    'content': document.querySelector('#id_content').value,
                    'area': parseInt(document.querySelector('#id_area').value),
                }
                xhr.send(JSON.stringify(data));

                // ---------------WEBSOCKET: ECHO MESSAGE---------------
                let area = document.getElementById('id_area')
                socket.send(JSON.stringify({
                    'content': document.querySelector('#id_content').value,
                    'area': area.options[area.selectedIndex].text,
                    'area_id': document.querySelector('#id_area').value
                }));
            }
        });
    </script>

Websockets,因此 django-channels 在 PythonAnywhere 上不受支持。