首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Django通道/ Daphne中的Websocket超时

Django通道/ Daphne中的Websocket超时
EN

Stack Overflow用户
提问于 2017-11-27 21:30:31
回答 1查看 9.7K关注 0票数 5

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

代码语言:javascript
复制
channels==1.1.8
daphne==1.3.0
Django==1.11.7

详情如下:

我试图使用Django通道和Daphne接口服务器保持持久的Websocket连接打开。我正在使用大多数默认参数:daphne -b 0.0.0.0 -p 8000 my_app.asgi:channel_layer启动Daphne。

在浏览器中空闲一段时间后,我看到连接正在关闭,大约超过20秒。与断开一起发送的CloseEventcode值为1006 (异常闭包),没有reason集,wasClean设置为false。这应该是服务器关闭连接,而不发送显式关闭帧。

Daphne具有--ping-interval--ping-timeout标志,默认值分别为20秒和30秒。对于前者,这被记录为“WebSocket必须处于空闲状态的秒数”,对于前者,对于前者来说,这是“WebSocket关闭之前的秒数”,对于后者来说,如果没有对保持活动ping的响应,则为“秒数”。我阅读这篇文章时,Daphne将等待WebSocket空闲20秒才发送ping,如果30秒后没有收到响应,则关闭Websocket。相反,我看到的是在空闲20秒后连接被关闭。(经过三次默认尝试,分别在20081ms、20026ms和20032ms之后关闭)

如果我将服务器更改为使用daphne -b 0.0.0.0 -p 8000 --ping-interval 10 --ping-timeout 60 my_app.asgi:channel_layer启动,则连接仍将关闭,空闲时间约为20秒。(经过三次尝试,更新后的pings,19892ms,20011ms,19956ms后关闭)

代码如下:

consumer.py

代码语言:javascript
复制
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

代码语言:javascript
复制
from channels.routing import route

from . import consumers

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

demo.js

代码语言:javascript
复制
// 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

代码语言:javascript
复制
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

代码语言:javascript
复制
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'asgi_redis.RedisChannelLayer',
        'ROUTING': 'main.routing.channel_routing',
        'CONFIG': {
            'hosts': [
                'redis://redis:6379/2',
            ],
            'symmetric_encryption_keys': [
                SECRET_KEY,
            ],
        }
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-11-28 00:01:08

根本的问题是接口服务器前面的nginx代理。代理被设置为proxy_read_timeout 20s;。如果有从服务器生成的保持活动的pings,则不会将其计算为上游读取超时。将此超时时间增加到更大的值,可以使Websocket保持更长的打开时间。我把proxy_connect_timeoutproxy_send_timeout留在了20s

票数 12
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47520105

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档