Django通道间歇性运行的可能原因

Django通道间歇性运行的可能原因,django,websocket,django-channels,Django,Websocket,Django Channels,我正试图使用我的数字海洋Ubuntu18水滴上的Django频道,从附在覆盆子pi上的网络摄像头提供“视频”源。raspberry pi使用python websockets客户端和openCV快速发送jpeg图像(每0.05秒一次) 问题是,这将工作得很好,10分钟以上的流媒体播放效果非常好,帧速率也很好,我想我已经成功了。然后第二天,它将流大约10秒,然后浏览器上的帧速率变得非常滞后,然后停止接收图像。Raspberry pi然后从websocket断开连接,这似乎是我的服务器断开连接,我无

我正试图使用我的数字海洋Ubuntu18水滴上的Django频道,从附在覆盆子pi上的网络摄像头提供“视频”源。raspberry pi使用python websockets客户端和openCV快速发送jpeg图像(每0.05秒一次)

问题是,这将工作得很好,10分钟以上的流媒体播放效果非常好,帧速率也很好,我想我已经成功了。然后第二天,它将流大约10秒,然后浏览器上的帧速率变得非常滞后,然后停止接收图像。Raspberry pi然后从websocket断开连接,这似乎是我的服务器断开连接,我无法让它流超过几秒钟。为什么它会在某一天完美地工作,而不是第二天?没有明显的变化

我认为是我的服务器/频道/redis导致了问题: 在raspberry pi出现任何问题之前,浏览器上显示的视频将变得非常滞后,然后将完全停止获取新图像。几秒钟后,我的raspberry pi套接字将与websocket断开连接

我所尝试的:

  • 我已经对我的互联网连接进行了速度测试,它是50mb下降/15mb上升
  • 我尝试过将帧速率降低到非常慢的速度,比如每秒一张图像,但它仍然会停止工作
  • 我已经检查了我的内存和我的水滴和覆盆子pi的CPU。当崩溃发生时,两台机器上的CPU使用率通常低于25%,内存使用率通常低于40%
  • 我在下面显示的
    settings.py
    中更改了一些redis设置,似乎没有任何效果
什么会导致websocket间歇性运行?我已在settings.py中更改了我的redis设置,但我不确定还要更改或尝试调试什么

我在这里遵循了有关部署的教程:

频道代码:

Consumers.py

from channels.generic.websocket import AsyncWebsocketConsumer
#import urllib.parse as urlparse
#from urllib.parse import parse_qs
from channels.db import database_sync_to_async
from accounts.models import CustomUser
from rest_framework.authtoken.models import Token

#websocket for video connection
class StreamConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['device_id']
        print(self.room_name)
        self.room_group_name = 'stream_%s' % self.room_name
        self.token_passed = self.scope['query_string'].decode() #token passed in with ws://url/?token
        self.token_actual = await self.get_token(self.room_name) #go get actual token for this monitor

        # Join room group
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        #check to ensure tokens match, if they do, allow connection
        if self.token_passed == self.token_actual:
            await self.accept()
            print('True')
        else:
            print(self.token_passed)
            print(self.token_actual)
            await self.close()


    async def disconnect(self, close_code):
        # Leave room group
        print(close_code)
        print("closing")
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    @database_sync_to_async
    def get_token(self, monitor_serial):
        try:
            monitor = CustomUser.objects.get(deviceSerial=monitor_serial)
            token = Token.objects.get(user=monitor)
            return token.key
        except:
            return "user not available"
        



    # Receive message from WebSocket
    async def receive(self, bytes_data=None, text_data=None):
        # Send message to room group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'stream_data',
                'bytes_data': bytes_data,
                'text_data': text_data
            }
        )

    # Receive message from room group
    async def stream_data(self, event):
        bytes_data = event['bytes_data']
        text_data = event['text_data']
        # Send message to WebSocket
        await self.send(bytes_data=bytes_data, text_data=text_data)



class StatusConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['device_id']
        self.room_group_name = 'status_%s' % self.room_name
        self.token_passed = self.scope['query_string'].decode() #token passed in with ws://url/?token
        self.token_actual = await self.get_token(self.room_name) #go get actual token for this monitor

        # Join room group
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        #check to ensure tokens match, if they do, allow connection
        if self.token_passed == self.token_actual:
            await self.accept()
        else:
            print(self.token_passed)
            print(self.token_actual)
            await self.close()


    async def disconnect(self, close_code):
        # Leave room group
        print(close_code)
        print("closing")
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    @database_sync_to_async
    def get_token(self, monitor_serial):
        monitor = CustomUser.objects.get(deviceSerial=monitor_serial)
        token = Token.objects.get(user=monitor)
        return token.key


    # Receive message from WebSocket
    async def receive(self, text_data=None):
        # Send message to room group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'status_data',
                'text_data': text_data
            }
        )

    # Receive message from room group
    async def status_data(self, event):
        #bytes_data = event['bytes_data']
        text_data = event['text_data']
        # Send message to WebSocket
        #await self.send(bytes_data=bytes_data, text_data=text_data)
        await self.send(text_data=text_data)
import os

from django.conf.urls import url
from django.core.asgi import get_asgi_application


# Fetch Django ASGI application early to ensure AppRegistry is populated
# before importing consumers and AuthMiddlewareStack that may import ORM
# models.
try:
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "polysensesite.settings")
except:
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

django_asgi_app = get_asgi_application()

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter


from .consumers import StreamConsumer, StatusConsumer


application = ProtocolTypeRouter({
    # Django's ASGI application to handle traditional HTTP requests
    "http": django_asgi_app,

    # WebSocket chat handler
    "websocket": AuthMiddlewareStack(
        URLRouter([
            url(r"^stream/(?P<device_id>[\d\-]+)/$", StreamConsumer.as_asgi()),
            url(r"^status/(?P<device_id>[\d\-]+)/$", StatusConsumer.as_asgi()),
        ])
    ),
})
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("localhost", 6379)],
            "capacity": 5000,
            "expiry": 5,
        },
    },
}
asgi.py

from channels.generic.websocket import AsyncWebsocketConsumer
#import urllib.parse as urlparse
#from urllib.parse import parse_qs
from channels.db import database_sync_to_async
from accounts.models import CustomUser
from rest_framework.authtoken.models import Token

#websocket for video connection
class StreamConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['device_id']
        print(self.room_name)
        self.room_group_name = 'stream_%s' % self.room_name
        self.token_passed = self.scope['query_string'].decode() #token passed in with ws://url/?token
        self.token_actual = await self.get_token(self.room_name) #go get actual token for this monitor

        # Join room group
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        #check to ensure tokens match, if they do, allow connection
        if self.token_passed == self.token_actual:
            await self.accept()
            print('True')
        else:
            print(self.token_passed)
            print(self.token_actual)
            await self.close()


    async def disconnect(self, close_code):
        # Leave room group
        print(close_code)
        print("closing")
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    @database_sync_to_async
    def get_token(self, monitor_serial):
        try:
            monitor = CustomUser.objects.get(deviceSerial=monitor_serial)
            token = Token.objects.get(user=monitor)
            return token.key
        except:
            return "user not available"
        



    # Receive message from WebSocket
    async def receive(self, bytes_data=None, text_data=None):
        # Send message to room group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'stream_data',
                'bytes_data': bytes_data,
                'text_data': text_data
            }
        )

    # Receive message from room group
    async def stream_data(self, event):
        bytes_data = event['bytes_data']
        text_data = event['text_data']
        # Send message to WebSocket
        await self.send(bytes_data=bytes_data, text_data=text_data)



class StatusConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['device_id']
        self.room_group_name = 'status_%s' % self.room_name
        self.token_passed = self.scope['query_string'].decode() #token passed in with ws://url/?token
        self.token_actual = await self.get_token(self.room_name) #go get actual token for this monitor

        # Join room group
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        #check to ensure tokens match, if they do, allow connection
        if self.token_passed == self.token_actual:
            await self.accept()
        else:
            print(self.token_passed)
            print(self.token_actual)
            await self.close()


    async def disconnect(self, close_code):
        # Leave room group
        print(close_code)
        print("closing")
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    @database_sync_to_async
    def get_token(self, monitor_serial):
        monitor = CustomUser.objects.get(deviceSerial=monitor_serial)
        token = Token.objects.get(user=monitor)
        return token.key


    # Receive message from WebSocket
    async def receive(self, text_data=None):
        # Send message to room group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'status_data',
                'text_data': text_data
            }
        )

    # Receive message from room group
    async def status_data(self, event):
        #bytes_data = event['bytes_data']
        text_data = event['text_data']
        # Send message to WebSocket
        #await self.send(bytes_data=bytes_data, text_data=text_data)
        await self.send(text_data=text_data)
import os

from django.conf.urls import url
from django.core.asgi import get_asgi_application


# Fetch Django ASGI application early to ensure AppRegistry is populated
# before importing consumers and AuthMiddlewareStack that may import ORM
# models.
try:
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "polysensesite.settings")
except:
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

django_asgi_app = get_asgi_application()

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter


from .consumers import StreamConsumer, StatusConsumer


application = ProtocolTypeRouter({
    # Django's ASGI application to handle traditional HTTP requests
    "http": django_asgi_app,

    # WebSocket chat handler
    "websocket": AuthMiddlewareStack(
        URLRouter([
            url(r"^stream/(?P<device_id>[\d\-]+)/$", StreamConsumer.as_asgi()),
            url(r"^status/(?P<device_id>[\d\-]+)/$", StatusConsumer.as_asgi()),
        ])
    ),
})
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("localhost", 6379)],
            "capacity": 5000,
            "expiry": 5,
        },
    },
}
我在浏览器、消费者和raspberry pi上放置了一些打印语句,看起来一切正常,除了raspberry pi停止接收消息(但仍在尝试发送图像),然后插座突然关闭,我从Daphne收到以下消息:

127.0.0.1:37154 - - [30/May/2021:16:56:32] "WSDISCONNECT /stream/XXXXXXX/" - -
1001
closing
2021-05-30 16:56:33,364 ERROR    Exception inside application:
Traceback (most recent call last):
  File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/routing.py", line 71, in __call__
    return await application(scope, receive, send)
  File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/sessions.py", line 47, in __call__
    return await self.inner(dict(scope, cookies=cookies), receive, send)
  File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/sessions.py", line 254, in __call__
    return await self.inner(wrapper.scope, receive, wrapper.send)
  File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/auth.py", line 181, in __call__
    return await super().__call__(scope, receive, send)
  File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/middleware.py", line 26, in __call__
    return await self.inner(scope, receive, send)
  File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/routing.py", line 160, in __call__
    send,
  File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/consumer.py", line 94, in app
    return await consumer(scope, receive, send)
  File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/consumer.py", line 59, in __call__
    [receive, self.channel_receive], self.dispatch
  File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/utils.py", line 51, in await_many_dispatch
    await dispatch(result)
  File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/consumer.py", line 73, in dispatch
    await handler(message)
  File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels/generic/websocket.py", line 198, in websocket_receive
    await self.receive(bytes_data=message["bytes"])
  File "./polysensesite/consumers.py", line 69, in receive
    'text_data': text_data
  File "/home/polysense/polysensesite/psvirtualenv/lib/python3.6/site-packages/channels_redis/core.py", line 667, in group_send
    key, min=0, max=int(time.time()) - int(self.expiry)
aioredis.errors.ConnectionForcedCloseError

很抱歉这么抽象,但你必须先弄清楚什么已经停止了?摄像头驱动程序到读卡器?数据到套接字?插座到水滴?水滴到浏览器?数据是从一个站点传递到下一个站点的,所以通过使用打印语句/日志记录来确定阻塞的位置。这就是我调试时遇到的问题。raspberry pi以正确的速率发送图像,直到我的服务器由于某种原因断开websocket。追踪断开连接原因的最佳方法是什么?当我看daphne日志时,它只是说断开了1006。没有理由。这是互联网,你应该处理断开连接、重试等等。任何目的地都可能在任何时间点断开连接。只需清理、重新连接,然后告诉相机暂停流媒体或继续使用图像并丢弃它们——无论哪种方式效果最好。我想解决的问题并不是断开连接,也许我应该调整我上面的问题,因为它不清楚。有一天,图像将以高帧频长时间传输,不会出现任何问题,然后其他几天,帧速率将开始非常缓慢和滞后,并将继续滞后,直到插座刚刚断开连接,覆盆子pi仍以相同的速率发送,但这几乎就像服务器/redis/频道无法处理它一样。我找不到任何日志或任何有助于我理解为什么会发生这种情况的东西。明白了。所以,首先要详细说明什么是套接字,在我看来,有两种:一种是从RPI到droplet,另一种是从droplet到浏览器。哪一个堵塞了?第二:当没有图像消费者时,RPI发送的图像会发生什么变化?它们是储存的吗?如果是,在哪里?