django channels 运行一段时间后自动断开

我做了一个目标检测的程序,

  1. 发送端一直检测摄像头的图形,使用websocket 发送检测完成的图像和结果给 django channel
  2. django channel 作为 后端 把接收到的结果 用consumer 给前端页面
  3. 前端页面 在 body 的onload 中 使用js 处理传过来的数据并显示在 img 中

前端核心代码如下:

var ws = new WebSocket(websocket_url);                                                
ws.onmessage = function (evt)                
 { // 处理逻辑 }

https://github.com/django/channels/issues/1981

比较迷惑,而且异常全都是 autobahn.exception.Disconnected

在consumer 中的code 如下:

try:    
    await super().send(json_text_data)
except autobahn.exception.Disconnected:
    await self.disconnect()
    await self.close()

使用的是django channel 4.0 ,发现如果是只用self.close() 是下面的报错,一段时间后还要清除 channel_layer 中的东西应该是

Application instance <Task pending name='Task-2388' coro=<ASGIStaticFilesHandler.__call__() running at C:\Users\wyaning.conda\envs\qalab\lib\site-packages\django\contrib\staticfiles\handlers.py:101> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x0000027CDCB1B5B0>()]>> for connection <WebSocketProtocol client=['10.167.118.211', 58719] path=b'/ws/ObjectDetection/ObjectDetection0/'> took too long to shut down and was killed.

如果是先用disconnect 那么就是程序发送端也就断了,需要10秒左右重新建立链接,很头大。

报错如下:

img

https://github.com/django/channels/issues/1119

disconnect 和 close 的文档怎么描述两个接口的。

  1. 问题描述
    上线一个新的环境,发现原来的ws连接上之后,会自动断开。查看日志发现,连接其实已经成功,但是莫名就断开了
    日志参考如下

127.0.0.1:40148 - - [23/Sep/2021:18:58:18] "WSCONNECTING /operation/api/ws/smart_station/car_real_time/609a646968206d11a655d3e3/" - -
127.0.0.1:40148 - - [23/Sep/2021:18:58:18] "WSCONNECT /operation/api/ws/smart_station/car_real_time/609a646968206d11a655d3e3/" - -
==== 连接成功
==== 连接成功, 并且来到了最后一行
127.0.0.1:40148 - - [23/Sep/2021:18:58:19] "WSDISCONNECT /operation/api/ws/smart_station/car_real_time/609a646968206d11a655d3e3/" - -
127.0.0.1:36798 - - [23/Sep/2021:19:01:41] "WSCONNECTING /operation/api/ws/smart_station/car_real_time/609a646968206d11a655d3e3/" - -
从上述的日志中发现,我自己打印的连接上之后的日志全部打印出来了,并且整个连接也成功连上了,但是很快就立刻断开了。并且断开的时候并没有调用我的consumer中的disconnect方法,相应的日志也没有打印出来。

  1. 排查
    nginx配置有问题???
    经过运维排查,并没有发现这个环境配置与没有问题的环境有任何区别,并且该环境曾经也部署过这个服务,ws连接一直是正常的。

排查修改
排查修改过的代码,发现redis配置发生过变更,换了新的redis实例。与运维同学沟通,发现新的redis的版本为4.0,而原先的redis是5.0版本

  1. 解决
    使用channel时配套使用了channel_redis,但是发现有内存泄漏问题,主要是由于channel_redis本地缓存的queque的大小没有做限制,导致的内存泄漏,该问题解决的版本在channel_redis 3.1.0 。所以我们对channel_redis这个包做了升级。
    channel_redis 3.1.0 要求使用5.0版本以上的redis,所以当检测redis版本不对时,就会导致ws连接的断开。
    修改redis配置为一个5.0版本的实例,ws连接就正常了。

作者:Jayce_xi
链接:http://events.jianshu.io/p/c2d31862901d
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Django Channels是一个基于Django的WebSockets和HTTP长轮询应用程序框架。如果你的Django Channels应用程序在一段时间后自动断开,可能有以下几个原因:

超时:默认情况下,Django Channels的WebSocket连接和HTTP长轮询连接都有一个超时时间。如果在规定时间内没有收到任何消息,连接将被关闭。可以通过在settings.py中设置CHANNELS_WS_KEEPALIVE和CHANNELS_WS_TIMEOUT来延长超时时间。

内存泄漏:如果你的应用程序中存在内存泄漏,可能会导致连接自动断开。可以使用内存分析工具来检测内存泄漏,并尝试修复它们。

代理:如果你的应用程序在代理后面运行,可能会导致连接自动断开。在这种情况下,你可能需要配置代理以正确处理WebSocket和HTTP长轮询连接。

负载均衡:如果你的应用程序在负载均衡器后面运行,可能会导致连接自动断开。在这种情况下,你可能需要配置负载均衡器以正确处理WebSocket和HTTP长轮询连接。
为了解决这个问题,你可以尝试以下几个步骤:

延长超时时间:在settings.py中设置CHANNELS_WS_KEEPALIVE和CHANNELS_WS_TIMEOUT来延长超时时间,以确保连接不会因为超时而自动断开。

检测内存泄漏:使用内存分析工具来检测内存泄漏,并尝试修复它们。

配置代理和负载均衡器:如果你的应用程序在代理或负载均衡器后面运行,需要配置它们以正确处理WebSocket和HTTP长轮询连接。

调整连接池:如果你使用的是Channels内置的连接池,可以尝试调整连接池的大小或其他参数,以确保连接池中始终有足够的连接可用。
总之,如果你的Django Channels应用程序在一段时间后自动断开,需要仔细检查可能的原因,并尝试进行相应的调整和修复。

该回答引用GPTᴼᴾᴱᴺᴬᴵ
这个问题可能是由于WebSocket连接在一段时间内没有活动而被服务器断开导致的。根据异常信息来看,autobahn.exception.Disconnected表示WebSocket连接已断开。
-
为了解决这个问题,可以考虑在代码中增加一些措施来保持WebSocket连接的活跃性,以避免连接因为长时间没有活动而被服务器断开。
-
一种常见的方法是定期向客户端发送一些数据,以保持连接的活跃性。可以在consumer中添加一个定时器,每隔一段时间向客户端发送一个空的消息,以确保WebSocket连接仍然处于活动状态。例如:

import asyncio
from channels.consumer import AsyncWebsocketConsumer

class MyConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        await self.accept()
        # 在连接成功后,启动定时器,每隔30秒发送一个空消息
        self.send_heartbeat()

    async def send_heartbeat(self):
        while self.websocket.connected:
            try:
                await self.send("")
                await asyncio.sleep(30)
            except:
                # 连接已断开
                break

    async def receive(self, text_data=None, bytes_data=None):
        # 处理接收到的消息
        pass

    async def disconnect(self, code):
        # 关闭连接
        pass


在这个示例中,我们在连接成功后启动了一个定时器,每隔30秒发送一个空消息。如果连接已断开,定时器会停止发送消息。

另外,还可以在服务器端设置WebSocket的keepalive选项,让服务器定期发送Ping帧来保持WebSocket连接的活跃性。具体做法可以参考Django Channels文档中的介绍:https://channels.readthedocs.io/en/stable/topics/websockets.html#keeping-connections-alive

总之,要解决这个问题,关键是要保持WebSocket连接的活跃性,避免连接因为长时间没有活动而被服务器断开。定期发送心跳消息或者启用WebSocket的keepalive选项都是有效的方法。

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 关于该问题,我找了一篇非常好的博客,你可以看看是否有帮助,链接:Django channels

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^