2017-11-27 9 views
0

質問:私のDaphneの設定や私の消費者コード、クライアントコードで何が間違っていますか?以下Djangoチャンネル/ DaphneでWebsocketタイムアウトが発生する

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

詳細:


私はDjangoのチャネルとダフネ・インターフェース・サーバーを使用して開いた永続的なのWebSocket接続を維持しようとしています。私は大部分がデフォルトの引数であるDaphneを起動しています:daphne -b 0.0.0.0 -p 8000 my_app.asgi:channel_layer

ブラウザで何らかのアイドル時間が経過した後、接続が閉じて20秒も経っています。切断されて送信されたCloseEventは、codeの値が1006(異常終了)であり、reasonが設定されておらず、wasCleanがfalseに設定されています。このは、明示的なクローズフレームを送信せずに接続を閉じるサーバーである必要があります。

Daphne CLIには、それぞれデフォルト値の20秒と30秒の--ping-interval--ping-timeoutフラグがあります。これは、「キープアライブpingが送信される前にWebSocketがアイドルでなければならない秒数」と、「キープアライブpingへの応答がない場合はWebSocketが閉じられるまでの秒数」として文書化されています。私はDaphneがWebSocketがpingを送信するために20秒間アイドル状態になるまで待機し、30秒後に応答がない場合はWebsocketを閉じます。私が見ているのは、接続が20秒間アイドル状態になってから閉じてしまうことです。

daphne -b 0.0.0.0 -p 8000 --ping-interval 10 --ping-timeout 60 my_app.asgi:channel_layerで起動するようにサーバーを変更した場合、アイドル時間は約20秒ですが、接続はまだ閉じています(アイドル時間は約20秒です)。

コード(更新済みのpingと3回の試行後、19892ms、20011ms、19956ms後に閉じ)以下:


consumer.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, 
      ], 
     } 
    } 
} 
+0

** asgi.py **と** settings.py ** ** –

+0

asgi.pyが表示されます。より多くを突きつけた後、私は問題が部分的に20秒に設定されたプロキシのタイムアウトを持つnginxプロキシと思う。しかし、ダフネが15秒後にpingを送っても大丈夫なはずですが、何か他のものはまだ間違っていると思います。 – wmorrell

+0

テンプレートに** WebSocket **の代わりに** channels.WebSocketBridge **クラスを使用することをお勧めします –

答えて

0

根本的な問題は、インタフェースサーバの前にnginxのプロキシであることが判明しました。プロキシはproxy_read_timeout 20s;に設定されました。サーバからキープアライブpingが生成された場合、アップストリームの読み取りタイムアウトにカウントされませんでした。このタイムアウト値を大きくすると、Websocketはより長く開いたままになります。私はproxy_connect_timeoutproxy_send_timeout20sに保ちました。

関連する問題