django / channels

Developer-friendly asynchrony for Django
https://channels.readthedocs.io
BSD 3-Clause "New" or "Revised" License
6.08k stars 800 forks source link

Why is the channel automatically disconnected after sending for a period of time #1981

Closed wynshiter closed 1 year ago

wynshiter commented 1 year ago

https://github.com/django/channels/issues/1466#issuecomment-1441157396

And basically,autobahn.exception.Disconnected

wynshiter commented 1 year ago

if i use self.close() instead of self.disconnect

after a while i get the error messge:

Application instance <Task pending name='Task-1' 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 0x00000147B7FF0130>()]>> for connection <WebSocketProtocol client=['10.167.118.211', 56636] path=b'/ws/ObjectDetection/ObjectDetection0/'> took too long to shut down and was killed.

wynshiter commented 1 year ago

just test it will close for every minutes。。。 here is my Chinese ask :https://ask.csdn.net/questions/7894068

wynshiter commented 1 year ago

i use Daphne in django and use like this :

python manage.py runserver --http_timeout=999 seems not working

wynshiter commented 1 year ago

Sorry to submitted a lot of information. This is mainly because I didn't have any clue when debugging. At present, we have finally found the reason. When using python websockets to send messages to django channel, The ping_timeout parameter will cause the django channel to send on a closed protocol.

https://websockets.readthedocs.io/en/stable/reference/client.html

my package is

absl-py 1.2.0 asgiref 3.5.2 asttokens 2.2.1 async-timeout 4.0.2 attrs 22.1.0 autobahn 22.7.1 Automat 22.10.0 autopep8 2.0.0 backcall 0.2.0 backports.functools-lru-cache 1.6.4 backports.zoneinfo 0.2.1 brotlipy 0.7.0 cachetools 5.2.0 certifi 2022.12.7 cffi 1.15.1 channels 4.0.0 channels-redis 4.0.0 charset-normalizer 2.1.1 colorama 0.4.6 coloredlogs 15.0.1 comm 0.1.2 constantly 15.1.0 contourpy 1.0.5 cryptography 37.0.1 cycler 0.11.0 daphne 4.0.0 debugpy 1.6.6 decorator 5.1.1 Deprecated 1.2.13 Django 4.1.3 executing 1.2.0 flatbuffers 23.3.3 fonttools 4.37.3 google-auth 2.11.1 google-auth-oauthlib 0.4.6 grpcio 1.49.1 humanfriendly 10.0 hyperlink 21.0.0 idna 3.4 importlib-metadata 6.0.0 incremental 22.10.0 ipykernel 6.21.2 ipython 8.10.0 jedi 0.18.2 jupyter_client 8.0.3 jupyter_core 5.2.0 kiwisolver 1.4.4 Markdown 3.4.1 MarkupSafe 2.1.1 matplotlib 3.6.0 matplotlib-inline 0.1.6 mpmath 1.3.0 msgpack 1.0.4 nest-asyncio 1.5.6 numpy 1.23.3 oauthlib 3.2.1 onnxruntime 1.14.1 opencv-python 4.6.0.66 packaging 23.0 pandas 1.5.0 parso 0.8.3 pickleshare 0.7.5 Pillow 9.2.0 pip 22.2.2 platformdirs 2.5.2 prompt-toolkit 3.0.36 protobuf 3.19.5 psutil 5.9.4 pure-eval 0.2.2 pyasn1 0.4.8 pyasn1-modules 0.2.8 pycodestyle 2.9.1 pycparser 2.21 Pygments 2.14.0 pyOpenSSL 22.0.0 pyparsing 3.0.9 pyreadline3 3.4.1 PySocks 1.7.1 python-dateutil 2.8.2 pytz 2022.2.1 pywin32 304 PyYAML 6.0 pyzmq 25.0.0 redis 4.3.4 requests 2.28.1 requests-oauthlib 1.3.1 rsa 4.9 scipy 1.9.1 seaborn 0.12.0 service-identity 21.1.0 setuptools 65.3.0 six 1.16.0 sqlparse 0.4.3 stack-data 0.6.2 sympy 1.11.1 tensorboard 2.10.0 tensorboard-data-server 0.6.1 tensorboard-plugin-wit 1.8.1 thop 0.1.1.post2209072238 tomli 2.0.1 torch 1.12.1 torchaudio 0.12.1 torchvision 0.13.1 tornado 6.2 tqdm 4.64.1 traitlets 5.9.0 Twisted 22.10.0 twisted-iocpsupport 1.0.2 txaio 22.2.1 typing_extensions 4.3.0 tzdata 2022.6 urllib3 1.26.11 wcwidth 0.2.6 websockets 10.3 Werkzeug 2.2.2 wheel 0.37.1 win-inet-pton 1.1.0 wrapt 1.14.1 zipp 3.13.0 zope.interface 5.5.1

the websockets send code likes:

# trying to always send the data to django channel
async def main_logic(opt,model):

    ws_url = 'ws://127.0.0.1:8000/ws/ObjectDetection/ObjectDetection/'
    async for websocket in websockets.connect(ws_url, open_timeout=None, ping_interval=1, ping_timeout=120, close_timeout=1):
        try:
            # in function run i send package 
            await run(websocket,model)
            message =await websocket.recv()
            print(message)
        except websockets.ConnectionClosed:
            continue

def main():

    asyncio.get_event_loop().run_until_complete(main_logic(opt,model))
    asyncio.get_event_loop().run_forever()

if __name__ == "__main__":
    main()

and here is my consumer ,heart beat not send back to the send-end the front end also use a websocket to get data from django channel seem not be affected.

class ObjectDetectionConsumer(AsyncWebsocketConsumer):

    async def connect(self):
        self.room_group_name_ObjectDetection = 'ObjectDetection'
        self.room_group_name_view = 'view'

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

        await self.accept()

    async def disconnect(self, close_code):
        # Leave room group
        await self.channel_layer.group_discard(
            self.room_group_name_ObjectDetection,
            self.channel_name
        )
        await self.channel_layer.group_discard(
            self.room_group_name_view,
            self.channel_name
        )
        raise StopConsumer()

    # Receive message from WebSocket
    async def receive(self, text_data):

        try:

            await self.channel_layer.group_send(
                self.room_group_name_ObjectDetection,
                {'type':'heartbeat.message',
                'message':'',}
            )
            await self.channel_layer.group_send(             
                    self.room_group_name_view,             
                    {             
                        'type': 'video1.message',             
                        'message': text_data,             
                    }            
                )

        except Exception as e:
            print(e)               
            await self.disconnect({'code': 400})             
            await self.close(400)

    # Receive message from sender group
    async def video1_message(self, event):
        # print(1)
        message = event['message']
        message_dict = json.loads(message)
        json_text_data = json.dumps({... })

        try:
            await self.send(text_data=json_text_data) 
        except Exception as e:
            #autobahn.exception.Disconnected
            #print('autobahn.exception.Disconnected')
            print(e)
            time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')     
            print(time)
            await self.close()   
            await self.disconnect('400')    

    async def heartbeat_message(self, event): 
        if self.websocket_connect:
            try:          
                await self.send("--pong--")
            except Exception as e:
            # 连接已断开
                print(e)
                pass
carltongibson commented 1 year ago

Glad you worked it out @wynshiter