组和通道层使流程停止而不会引发异常

时间:2020-03-03 13:49:10

标签: python django redis django-channels asgi

从这个非常简单的工作代码示例开始:

from channels.generic.websocket import JsonWebsocketConsumer

class IsacomptaManagementFeesConsumer(JsonWebsocketConsumer):
    pass

从javascript连接到此websocket使用者时,此方法可以正常工作。连接已正确发出,并且我收到以下日志:

WebSocket HANDSHAKING /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38108]
WebSocket CONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38108]
WebSocket DISCONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38108]

现在,如果我将代码更改为以下代码,则可以使用组:

from channels.generic.websocket import JsonWebsocketConsumer

class IsacomptaManagementFeesConsumer(JsonWebsocketConsumer):
    groups = ['foobar']

然后,连接失败。 JavaScript控制台告诉我:

Firefox can’t establish a connection to the server at ws://antoine.cocoonr.hq:3001/manager/accounting/isacompta/2020/03/management-fees.ws.
error { target: WebSocket, isTrusted: true, srcElement: WebSocket, currentTarget: WebSocket, eventPhase: 2, bubbles: false, cancelable: false, defaultPrevented: false, composed: false, timeStamp: 7804, … }

服务器日志如下:

WebSocket HANDSHAKING /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38128]
WebSocket DISCONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38128]

虽然在服务器端没有异常。

我也可以不使用组而得到类似的行为。让我们来看一个更大的工作代码示例:

from channels.generic.websocket import JsonWebsocketConsumer

class IsacomptaManagementFeesConsumer(JsonWebsocketConsumer):

    def connect(self):
        print("one")
        self.accept()
        print("two")
        self.send_json({'text': "Foobar"})
        print("three")

此代码可以正常工作,连接已正确发出,这是服务器日志:

WebSocket HANDSHAKING /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38168]
one
WebSocket CONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38168]
two
three
WebSocket DISCONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38168]

但是,如果我对我的代码进行以下更改:

from asgiref.sync import async_to_sync
from channels.generic.websocket import JsonWebsocketConsumer

class IsacomptaManagementFeesConsumer(JsonWebsocketConsumer):

    def connect(self):
        print("one")
        self.accept()
        print("two")
        async_to_sync(self.channel_layer.send)(self.channel_name, {
            'type': 'foobar.send',
            'text': "Foobar",
        })
        print("three")

    def foobar_send(self, event):
        print("AAA")
        self.send_json({'text': event['text'])
        print("BBB")

然后,连接已正确发出,但立即关闭,并且进程停止,没有机会打印“三”,并且也不执行函数“ foobar_send”。

WebSocket HANDSHAKING /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38224]                                                                                                                                                                             
one                                                                                                                                                                                                                                                                             
WebSocket CONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38224]                                                                                                                                                                                 
two                                                                                                                                                                                                                                                                             
WebSocket DISCONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38224]

我不明白为什么没有打印出“三”字,也没有出现异常。这意味着self.channel_layers.send()会引发异常,而IsacomptaManagementFeesConsumer.connect()的调用方会默默捕获该异常?

写完最后一段之后,我决定尝试一下:

import traceback

from asgiref.sync import async_to_sync
from channels.generic.websocket import JsonWebsocketConsumer

class IsacomptaManagementFeesConsumer(JsonWebsocketConsumer):

    def connect(self):
        print("one")
        self.accept()
        print("two")
        try:
            async_to_sync(self.channel_layer.send)(self.channel_name, {
                'type': 'foobar.send',
                'text': "Foobar",
            })
        except Exception as e:
            print(e)
            traceback.print_stack()
        print("three")

    def foobar_send(self, event):
        print("AAA")
        self.send_json({'text': event['text'])
        print("BBB")

就是这样,有一个隐藏的“找不到文件”错误:

WebSocket HANDSHAKING /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:42562]
one
WebSocket CONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:42562]
two
[Errno 2] No such file or directory
  File "/usr/lib64/python3.6/threading.py", line 884, in _bootstrap
    self._bootstrap_inner()
  File "/usr/lib64/python3.6/threading.py", line 916, in _bootstrap_inner
    self.run()
  File "/usr/lib64/python3.6/threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/lib64/python3.6/concurrent/futures/thread.py", line 69, in _worker
    work_item.run()
  File "/usr/lib64/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/tony/.venvs/cocoonr/lib/python3.6/site-packages/channels/db.py", line 14, in thread_handler
    return super().thread_handler(loop, *args, **kwargs)
  File "/home/tony/.venvs/cocoonr/lib/python3.6/site-packages/asgiref/sync.py", line 277, in thread_handler
    return func(*args, **kwargs)
  File "/home/tony/.venvs/cocoonr/lib/python3.6/site-packages/channels/consumer.py", line 105, in dispatch
    handler(message)
  File "/home/tony/.venvs/cocoonr/lib/python3.6/site-packages/channels/generic/websocket.py", line 39, in websocket_connect
    self.connect()
  File "/home/tony/Workspace/cocoonr/billing/consumers.py", line 32, in connect
    traceback.print_stack()
three
WebSocket DISCONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:42562]

我将Django 3.0与Channels 2.4.0和Channels-redis 2.4.2一起使用。这是我的CHANNEL_LAYERS设置:

 CHANNEL_LAYERS = {                                                                                                                                                          
     'default': {                                                                                                                                                                                                                                                         
         'BACKEND': 'channels_redis.core.RedisChannelLayer',                                                                                                                                                                                                                
         'CONFIG': {                                                                                                                                                                                                                                                       
             'hosts': [                                                                                                                                                                                                                                            
                 {                                                                                                                                                                                                                                                 
                     'address': get_env('CHANNELS_REDIS_HOST',                                                                                                                                                                                          
                                        default='localhost:6379'),                                                                                                                                                                             
                     'password': get_env('CHANNELS_REDIS_PASSWORD',                                                                                                                                                                             
                                         default=None),                                                                                                                                                                                          
                     'db': 1,            
                 },                                                                                                                                  
             ],                                                                                                                                   
         },                                                                                                                                                        
     },                                                                                                                                                        
 }                                                                                                                                                                                                                                                                        

1 个答案:

答案 0 :(得分:0)

issue #1340已经解决了隐藏错误的事实。

从FileNotFoundError本身开始,它是由错误的address值引起的。应该以{{1​​}}而不是'redis://localhost:6379/'的形式传递。

相关问题