当前位置: 首页 > 知识库问答 >
问题:

Django频道/达芙妮中的Web套接字超时

仲霍英
2023-03-14

简短的问题版本:我在 Daphne 配置、消费者代码或客户端代码中做错了什么?

channels==1.1.8
daphne==1.3.0
Django==1.11.7

详情如下:

我试图使用Django通道和Daphne接口服务器保持持久的Web套接字连接打开。我使用默认参数启动Daphne:daphne-b0.0.0.0-p 8000my_app.asgi:channel_layer

我看到在浏览器中闲置一段时间后,连接关闭,时间不超过20秒。与断开连接一起发送的< code>CloseEvent的< code>code值为< code>1006(异常关闭),未设置< code>reason,并且< code>wasClean设置为false。这应该是服务器关闭连接,而不发送显式关闭帧。

Daphne CLI 具有 --ping 间隔--ping 超时标志,默认值分别为 20 秒和 30 秒。对于前者,这被记录为“WebSocket 在发送激活 ping 之前必须处于空闲状态的秒数”,对于后者,则记录为“如果没有响应保持连接 ping 时关闭 WebSocket 之前的秒数”。我读到这个是因为Daphne会等到WebSocket空闲20秒再发送ping,如果30秒后没有收到响应,就会关闭Websocket。相反,我看到的是连接在空闲 20 秒后关闭。(在三次尝试中,默认值,在 20081 毫秒、20026 毫秒和 20032 毫秒之后关闭)

如果我将服务器更改为使用< code > Daphne-b 0 . 0 . 0 . 0-p 8000-ping-interval 10-ping-time out 60 my _ app . asgi:channel _ layer 启动,连接仍然关闭,大约有20秒的空闲时间。(三次尝试更新pings后,在19892毫秒、20011毫秒、19956毫秒后关闭)

以下代码:

消费者.py

import logging

from channels import Group
from channels.generic.websockets import JsonWebsocketConsumer

from my_app import utilities

logger = logging.getLogger(__name__)

class DemoConsumer(JsonWebsocketConsumer):
    """
    Consumer echos the incoming message to all connected Websockets,
    and attaches the username to the outgoing message.
    """
    channel_session = True
    http_user_and_session = True

    @classmethod
    def decode_json(cls, text):
        return utilities.JSONDecoder.loads(text)

    @classmethod
    def encode_json(cls, content):
        return utilities.JSONEncoder.dumps(content)

    def connection_groups(self, **kwargs):
        return ['demo']

    def connect(self, message, **kwargs):
        super(DemoConsumer, self).connect(message, **kwargs)
        logger.info('Connected to DemoConsumer')

    def disconnect(self, message, **kwargs):
        super(DemoConsumer, self).disconnect(message, **kwargs)
        logger.info('Disconnected from DemoConsumer')

    def receive(self, content, **kwargs):
        super(DemoConsumer, self).receive(content, **kwargs)
        content['user'] = self.message.user.username
        # echo back content to all groups
        for group in self.connection_groups():
            self.group_send(group, content)

routing.py

from channels.routing import route

from . import consumers

channel_routing = [
    consumers.DemoConsumer.as_route(path=r'^/demo/'),
]

demo.js

// Tracks the cursor and sends position via a Websocket
// Listens for updated cursor positions and moves an icon to that location
$(function () {
  var socket = new WebSocket('ws://' + window.location.host + '/demo/');
  var icon;
  var moveTimer = null;
  var position = {x: null, y: null};
  var openTime = null;
  var lastTime = null;
  function sendPosition() {
    if (socket.readyState === socket.OPEN) {
      console.log('Sending ' + position.x + ', ' + position.y);
      socket.send(JSON.stringify(position));
      lastTime = Date.now();
    } else {
      console.log('Socket is closed');
    }
    // sending at-most 20Hz
    setTimeout(function () { moveTimer = null; }, 50);
  };
  socket.onopen = function (e) {
    var box = $('#websocket_box');
    icon = $('<div class="pointer_icon"></div>').insertAfter(box);
    box.on('mousemove', function (me) {
      // some browsers will generate these events much closer together
      // rather than overwhelm the server, batch them up and send at a reasonable rate
      if (moveTimer === null) {
        moveTimer = setTimeout(sendPosition, 0);
      }
      position.x = me.offsetX;
      position.y = me.offsetY;
    });
    openTime = lastTime = Date.now();
  };
  socket.onclose = function (e) {
    console.log("!!! CLOSING !!! " + e.code + " " + e.reason + " --" + e.wasClean);
    console.log('Time since open: ' + (Date.now() - openTime) + 'ms');
    console.log('Time since last: ' + (Date.now() - lastTime) + 'ms');
    icon.remove();
  };
  socket.onmessage = function (e) {
    var msg, box_offset;
    console.log(e);
    msg = JSON.parse(e.data);
    box_offset = $('#websocket_box').offset();
    if (msg && Number.isFinite(msg.x) && Number.isFinite(msg.y)) {
      console.log((msg.x + box_offset.left) + ', ' + (msg.y + box_offset.top));
      icon.offset({
        left: msg.x + box_offset.left,
        top: msg.y + box_offset.top
      }).text(msg.user || '');
    }
  };
});

asgi.py

import os
from channels.asgi import get_channel_layer

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_project.settings")

channel_layer = get_channel_layer()

settings.py

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'asgi_redis.RedisChannelLayer',
        'ROUTING': 'main.routing.channel_routing',
        'CONFIG': {
            'hosts': [
                'redis://redis:6379/2',
            ],
            'symmetric_encryption_keys': [
                SECRET_KEY,
            ],
        }
    }
}

共有1个答案

陈成济
2023-03-14

根本问题是接口服务器前面的nginx代理。代理设置为proxy_read_timeout 20s 。如果服务器生成了保活ping,则这些ping不会计入上游读取超时。将此超时值增加到更大的值可以使Websocket保持更长的打开时间。我将proxy_connect_timeoutproxy_send_timeou20秒

 类似资料:
  • 问题内容: 我试图理解SocketChannels和NIO。我知道如何使用常规套接字,以及如何制作一个简单的每客户端线程服务器(使用常规阻塞套接字)。 所以我的问题是: 什么是SocketChannel? 当使用SocketChannel而不是Socket时,我还能得到什么呢? 通道和缓冲区之间是什么关系? 什么是选择器? 文档中的第一句话是。那是什么意思? 我也阅读了本文档,但是不知何故…… 问

  • 问题内容: 我看到了一些原因(请参阅下面的堆栈跟踪)。也就是说,客户端无法在超时(30秒)内获得响应。现在我想知道如何解决它。 简单的解决方案是增加超时时间,但是我想首先了解根本原因。你有什么建议? 由java.net.SocketTimeoutException引起:读取超时 问题答案: 这些超时是否在闲置一段时间后发生?空闲时间过长后,连接池可能已过时。 如果是这样,有两种解决方法: 捕获套接

  • 在同一个集群中的2个ESB实例中,我们不断地获得套接字超时。在日志中打印的IP地址属于负载均衡器,它位于2个ESB实例的顶部。一段时间后,ES实例将进入不健康状态,并且不会服务任何请求。 TID:[-1][][2018-10-07 22:42:11,711]警告{org.apache.synapse.transport.passthru.sourcehandler}-读取请求后连接超时:http-

  • Hi想知道是否有办法设置套接字的超时,以及在连接握手之前重试的次数。我在非常糟糕的连接模式下测试了我的应用程序,我为我在Volley中的请求设置了重试策略为10秒,但SSL握手似乎是个问题,因为它有一个默认的超时设置为60秒,而Volley超时只被触发当套接字由于尝试次数或超时而失败时。 下面是我的方法: 我正在使用截击来提出我的请求,我实现这一点的方法是: 我还尝试设置了这样的超时:

  • 问题内容: 我创建了一个小型聊天程序,当客户端和服务器在同一台计算机(也可能是网络)上运行时,该程序可以完美运行。但是,一旦我尝试通过Internet连接到另一台计算机,套接字连接就会超时。这是由于防火墙/路由器等引起的吗? 以及如何通过互联网连接ServerSocket和套接字? 问题答案: 但是,一旦我尝试通过Internet连接到另一台计算机,套接字连接就会超时。这是由于防火墙/路由器等引起

  • 问题内容: 我想将数据发送到服务器,然后等待一分钟,然后关闭套接字。 怎么做? 问题答案: 你可以试试看 根据您的情况更改套接字的超时!此代码将发送一条消息,然后等待接收消息,直到达到超时!