繁体   English   中英

使用 Django 通道意外收到 WebSocket 断开连接

[英]WebSocket disconnect received unexpectedly by using Django Channels

使用 Django 通道向用户更新可能长时间运行的任务的当前状态,我面临着一个我想追踪的WebSocket DISCONNECT

设置看起来很简单。 settings.py定义了通道层:

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

routing.py中,我们基本上遵循 Channels 文档中的默认建议(该模式只是匹配一个 UUID,我们将其用作面向公众的标识符):

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.conf.urls import url
from lektomat.consumers import analysis

application = ProtocolTypeRouter({
    # (http->django views is added by default)
    "websocket": AuthMiddlewareStack(
        URLRouter([
            # Use a regex to match the UUID as the Django version with its '<uuid:analysis_id>' does not work for Channels.
            url(r"^ws/analysis/(?P<analysis_id>[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12})/$",
                analysis.AnalysisConsumer),
        ])),
})

消费者只是通过新建立的 websockets 连接发送一些初始数据并在各处记录信息:

import logging
import json
from channels.db import database_sync_to_async
from uuid import UUID
from typing import Tuple
from django.utils import timezone
from channels.generic.websocket import AsyncWebsocketConsumer
from myapp.models import AnalysisResult

logger = logging.getLogger(__name__)


class AnalysisConsumer(AsyncWebsocketConsumer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.analysis_id = None
        self.analysis_group_name = 'analysis_'

    async def connect(self):
        self.analysis_id = self.scope['url_route']['kwargs']['analysis_id']
        self.analysis_group_name = "analysis_{}".format(self.analysis_id)
        await self.channel_layer.group_add(self.analysis_group_name, self.channel_name)
        logger.info("%s – AnalysisConsumer: connect()ed and joined group %s.", str(timezone.now()), self.analysis_group_name)
        dummy_percent = 11
        dummy_text = 'Dummy'
        logger.debug("%s – Sending initial channel update to group %s with a %d percent and status_text=%s", str(timezone.now()), self.analysis_group_name, dummy_percent, dummy_text)
        await self.send(text_data=json.dumps({
            'progress_percent': progress_percent,
            'status_text': status_text
        }))
        await self.accept()

    async def disconnect(self, code):
        logger.info("%s – AnalysisConsumer: disconnecting with code=%s (internal group name=%s).", str(timezone.now()), code, self.analysis_group_name)
        await self.channel_layer.group_discard(self.analysis_group_name, self.channel_name)
        logger.info("%s – AnalysisConsumer: disconnect(%s)ed and left room %s", str(timezone.now()), code, self.analysis_group_name)

    async def receive(self, text_data=None, bytes_data=None):
        logger.info("%s – unexpectedly received data from the websocket, text_data=%s, bytes_data=%s", str(timezone.now()), text_data, str(bytes_data))

最后,客户端的 javascript 连接到 websocket 端点:

var ws_scheme = window.location.protocol == "https:" ? "wss" : "ws";
var ws_uri = ws_scheme + '://' + window.location.host + '/ws/analysis/' + '{{ result_id }}' + '/';
var socket = new WebSocket(ws_uri);
socket.onopen = function open() {
    let now = new Date();
    console.info(now.toLocaleString() + ':' + now.getMilliseconds() + ' – WebSocket connection created.');
};

socket.onmessage = function(e) {
    console.log("WebSocket message received.")
    const data = JSON.parse(e.data);
    console.log("WebSocket message: " + data.status_text + " at " + data.progress_percent + " percent.");
};

socket.onclose = function(e) {
    let now = new Date();
    console.error(now.toLocaleString() + ':' + now.getMilliseconds() + ' – Analysis socket closed with event code = ' + e.code + ' and reason=' + e.reason);
};

socket.onerror = function(error) {
    let now = new Date();
    let msg = now.toLocaleString() + ':' + now.getMilliseconds() + ' – WebSocket error: ' + error;
    console.error(msg);
}

Redis 后端已启动并正在运行。

但是:websocket 连接在启动后立即关闭。 更准确地说,浏览器的 JS 控制台日志(德语,错误的翻译是我的):

(!) Firefox cannot connect to server at ws://localhost:8000/ws/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b/
(!) 10.8.2020, 22:30:21:317 – WebSocket error: [object Event]
    onerror http://localhost:8000/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b:149
    (Async: EventHandlerNonNull)
    <anonym> http://localhost:8000/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b:146
(!) 10.8.2020, 22:30:21:319 – Analysis socket closed with event code = 1006 and reason=
    onclose http://localhost:8000/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b:143
    (Async: EventHandlerNonNull)
    <anonym> http://localhost:8000/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b:141

服务器控制台说:

request: <AsgiRequest: GET '/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b'>
HTTP GET /analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b 200 [1.37, 127.0.0.1:51562]
WebSocket HANDSHAKING /ws/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b/ [127.0.0.1:51574]
2020-08-10 20:30:20.549519+00:00 – AnalysisConsumer: connect()ed and joined group analysis_d222ebe1-5a2a-4797-9466-24db1de5d24b.
2020-08-10 20:30:20.610167+00:00 – Sending initial channel update to group analysis_d222ebe1-5a2a-4797-9466-24db1de5d24b with a 11 percent and status_text=Dummy
WebSocket DISCONNECT /ws/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b/ [127.0.0.1:51574]

这样,服务器收到连接请求,建立websocket连接并立即断开连接。 客户端在大约半秒后响应代码 1006 的错误(这并不能说明太多)。 消费者的disconnect()永远不会被调用。

谁可能会启动WebSocket DISCONNECT 它似乎不是任何应用程序代码。 对这里缺少的内容的指针表示赞赏。

该消费者在接受连接之前尝试发送消息。

我刚刚测试过,达芙妮抛出了这个异常:

 File "...site-packages/daphne/ws_protocol.py", line 193, in handle_reply
 "Socket has not been accepted, so cannot send over it"

客户端收到带有代码 1006 的 CloseEvent

这看起来与我在此处描述的问题相同。

断开连接之前服务器的最后一条消息是它正在尝试发送一些东西(大?)。 我发现发送大型 WebSocket 消息可能会导致断开连接并出现错误 1006。

您可以尝试配置您的 WebSocket 服务器以小块发送数据。 它对我的情况有所帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM