Django Channels - Websocket连接失败

3
我目前正在尝试设置我的Django Channels聊天应用程序,我已经按照最好的理解遵循了文档 - 当然,当出现问题时总会有解决方案,但我只是在努力弄清楚我在哪里出错了,无法使其正常工作。
到目前为止,我已经安装了Django Channels,在settings.py中正确配置了我的channel_layer,我已经参考了文档和其他在线教程,创建了我的consumer.py文件、utils.py、views.py和chat.html模板文件。
我还配置了nginx、gunicorn、daphne和redis,并确认所有这些服务都在运行。我已经安装了LetsEncrypt,并允许SSL端口(443用于https连接)。
我当前通过以下方式在生产服务器上访问我的Django网站:https://mydomain:8001,我的聊天直接链接配置为https://mydomain:8001/messenger。
(我已通过防火墙允许了端口8001)
以下是我从聊天页面收到的状态消息:
SUCCESS {response: "Successfully got the chat.", chatroom_id: 1}chatroom_id: 1response: "Successfully got the chat."__proto__: Object
(index):303 setupWebSocket: 1
(index):371 ChatSocket connecting..
WebSocket connection to 'wss://www.<mydomain>.com:8001/messenger/1/' failed: 
setupWebSocket @ (index):317
success @ (index):389
c @ jquery.min.js
fireWith @ jquery.min.js
l @ jquery.min.js
(anonymous) @ jquery.min.js
ChatSocket error Event {isTrusted: true, type: "error", target: WebSocket, currentTarget: WebSocket, eventPhase: 2}bubbles: falsecancelBubble: falsecancelable: falsecomposed: falsecurrentTarget: WebSocket {url: "wss://<mydomain>:8001/messenger/1/", readyState: 3, bufferedAmount: 0, onopen: null, onOpen: ƒ}defaultPrevented: falseeventPhase: 0isTrusted: truepath: []returnValue: truesrcElement: WebSocket {url: "wss://<mydomain>:8001/messenger/1/", readyState: 3, bufferedAmount: 0, onopen: null, onOpen: ƒ}target: WebSocket {url: "wss://<mydomain>:8001/messenger/1/", readyState: 3, bufferedAmount: 0, onopen: null, onOpen: ƒ}timeStamp: 43973.62500001327type: "error"__proto__: Event
(index):357 Chat socket closed.

非常感谢您提供的帮助,如果您能够指导我如何解决这个问题,我将不胜感激。如果您能告诉我应该查看哪些文件,我将非常乐意更新我的聊天应用程序中任何所需文件的内容来回答您的问题。 :-)
谢谢!
文件内容:
nginx.conf
server {
    listen 80;
    server_name myapp mydomain.com www.mydomain.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/myapp;
    }

    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://unix:/home/myapp/myapp.sock;
    }
    
    location /ws/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_redirect off;
        proxy_pass https://127.0.0.1:8001;
    }
    
    location /wss/ {
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_pass http://127.0.0.1:8001;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
        }
}

routing.py

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.urls import path, re_path

from messenger.consumers import ChatConsumer


application = ProtocolTypeRouter({
    'websocket': AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter([
                
                    path('messenger/<room_id>/', ChatConsumer),
                    
            ])
        )
    ),
})

settings.py

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}

我在控制台收到的错误信息如下:(index):317 WebSocket连接到'wss://www.mydomain.com:8001/messenger/1/'失败:

(其中mydomain.com是我实际输入的域名 :)

journalctl中最新的Daphne日志

Jun 21 14:22:56 myhostname.com python3[36088]: 2021-06-21 14:22:56,576 INFO     Starting server at ssl:8001:privateKey=/etc/letsencrypt/live/mydomain.com/privkey.pem:certKey=/etc/let>
Jun 21 14:22:56 myhostname.com python3[36088]: 2021-06-21 14:22:56,576 INFO     HTTP/2 support not enabled (install the http2 and tls Twisted extras)
Jun 21 14:22:56 myhostname.com python3[36088]: 2021-06-21 14:22:56,577 INFO     Configuring endpoint ssl:8001:privateKey=/etc/letsencrypt/live/mydomain.com/privkey.pem:certKey=/etc/l>
Jun 21 14:22:56 myhostname.com python3[36088]: 2021-06-21 14:22:56,584 INFO     Listening on TCP address 0.0.0.0:8001

Daphne日志中的错误

Jun 21 14:38:51 myhostname.com python3[36088]: Traceback (most myhostname.com python3[36088]:   File "/usr/local/lib/python3.6/site-packages/channels/routing.py", line 71, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]:     return await application(scope, receive, send)
Jun 21 14:38:51 myhostname.com python3[36088]:   File "/usr/local/lib/python3.6/site-packages/channels/security/websocket.py", line 37, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]:     return await self.application(scope, send, receive)
Jun 21 14:38:51 myhostname.com python3[36088]:   File "/usr/local/lib/python3.6/site-packages/channels/sessions.py", line 47, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]:     return await self.inner(dict(scope, cookies=cookies), receive, send)
Jun 21 14:38:51 myhostname.com python3[36088]:   File "/usr/local/lib/python3.6/site-packages/channels/sessions.py", line 254, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]:     return await self.inner(wrapper.scope, receive, wrapper.send)
Jun 21 14:38:51 myhostname.com python3[36088]:   File "/usr/local/lib/python3.6/site-packages/channels/auth.py", line 181, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]:     return await super().__call__(scope, receive, send)
Jun 21 14:38:51 myhostname.com python3[36088]:   File "/usr/local/lib/python3.6/site-packages/channels/middleware.py", line 26, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]:     return await self.inner(scope, receive, send)
Jun 21 14:38:51 myhostname.com python3[36088]:   File "/usr/local/lib/python3.6/site-packages/channels/routing.py", line 160, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]:     send,
Jun 21 14:38:51 myhostname.com python3[36088]:   File "/usr/local/lib/python3.6/site-packages/asgiref/compatibility.py", line 33, in new_application
Jun 21 14:38:51 myhostname.com python3[36088]:     instance = application(scope)
Jun 21 14:38:51 myhostname.com python3[36088]:   File "/usr/local/lib/python3.6/site-packages/channels/generic/websocket.py", line 159, in __init__
Jun 21 14:38:51 myhostname.com python3[36088]:     super().__init__(*args, **kwargs)
Jun 21 14:38:51 myhostname.com python3[36088]: TypeError: object.__init__() takes no parameters


1
嗨。嗯,很难从哪里开始说起。您是否在nginx中有适当的websocket连接配置?另外,您是否配置了带有“www。”的域名?检查daphne日志-请求是否到达后端或未到达?也许您可以在那里看到任何错误。此外,请检查浏览器中的开发工具-它是否说明了连接失败的原因?可能需要附上相关的nginx配置,以及django asgi路由。 - Alexandr Tatarinov
你好,亚历山大。我刚看到你的回复。非常感谢你的回复!:-) 我将在我的问题中更新nginx中的websocket配置以及routing.py文件。连接失败没有给出任何原因,控制台中唯一打印的错误是“Websocket connection to wss://mydomain:8001/ failed.” 这就是我遇到困难的地方,因为这个错误很普遍,也不是很有帮助哈哈! :-) - Jarjar95
我现在已经更新了我的原始问题,包括您请求的两个文件的内容 :-) 以及我在控制台中收到的确切错误。 - Jarjar95
3个回答

8

我认为问题出在配置上。在nginx中,您必须指定URL前缀,而不是协议,也不需要同时添加wswss。nginx配置文件应该类似于这样:

location /ws/ {
    proxy_set_header Host               $http_host;
    proxy_set_header X-Real-IP          $remote_addr;
    proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host   $server_name;
    proxy_set_header X-Forwarded-Proto  $scheme;
    proxy_set_header X-Url-Scheme       $scheme;
    proxy_redirect off;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_pass http://127.0.0.1:8001;
}

因此,要通过Websocket协议连接,您必须访问 wss://www.<mydomain>.com:8001/ws/ 下的某些内容。为了使其工作,您的ASGI路由也必须包括ws前缀。另外,您应该在消费者上使用as_asgi()。(https://channels.readthedocs.io/en/stable/topics/routing.html

application = ProtocolTypeRouter({
    'websocket': AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter([
                    path('ws/messenger/<room_id>/', ChatConsumer.as_asgi()), 
            ])
        )
    ),
})

现在您应该可以连接到wss://www.<mydomain>.com:8001/ws/messenger/1/(不要忘记ws!)。


啊,非常感谢!我现在要测试这个解决方案,并感谢您抽出时间向我解释,这很清晰明了!今天下午我会提供更新结果 :-) - Jarjar95
所以我已经测试了你的解决方案 - 我仍然收到之前相同的错误,即WebSocket连接失败。这是我收到的错误,更新链接指向 /ws/ (index):317 WebSocket连接到 'wss://www.mydomain.com/ws/messenger/1/' 失败。 - Jarjar95
你有按照我在评论中提出的要求检查过Daphne日志吗?请求是否到达了Daphne?它是否正在运行?同时,请检查Nginx日志中是否有错误。如果存在权限问题,您将无法启动Daphne,此外,您需要使用的用户将针对您的设置进行特定配置,没有适用于所有服务器的“魔术”Django用户。 - Alexandr Tatarinov
我可以问一下吗 - 我是否需要在settings.py文件中启用Daphne的日志记录?还是Daphne会自动在服务器上创建日志,以便我可以访问?我将在明天晚上回家后立即检查日志! - Jarjar95
嗨,亚历山大,我已经更新了我的原始问题,并提供了Daphne日志的输出 - 服务正在运行并侦听0.0.0.0:8001。不确定消息“Http/2未启用”是否会阻止连接正常工作? Daphne.service正在运行,我可以通过https://<mydomain>:8001/messenger/访问我的Django聊天应用程序页面,但仍无法连接。 - Jarjar95
显示剩余6条评论

0

终于让Django Channels工作了(终于成功建立了我的Websocket连接)-非常感谢Alexandr的帮助。

从日志中,我发现了以下错误:ERROR: TypeError: object.__init__() takes exactly one argument (the instance to initialize)

我忘记在调用ChatConsumer的地方添加as_asgi()。所以我将我的路由路径从:

path('ws/messenger/<room_id>/', ChatConsumer),

改为

path('ws/messenger/<room_id>/', ChatConsumer.as_asgi()),

现在我的Websocket已经打开并成功连接!真是美好的一天!

再次感谢Alexandr的所有帮助!


哇,那个很容易被忽视。如果我能够帮助,请接受我的答案。 - Alexandr Tatarinov
谢谢Alexandr,我已经接受了你的答案!! :-) - Jarjar95
可能值得更新您的答案,因为我调整了路由路径 :-) 我试图提出一个编辑建议,但在StackOverflow上遇到了一个错误,提示“建议的编辑队列已满”。 - Jarjar95

0
    proxy_pass http://localhost:10001;
    proxy_set_header Host $host;
    # WebSocket support
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    # ...other WebSocket settings

1
感谢您为Stack Overflow社区做出的贡献。这可能是一个正确的答案,但如果您能提供代码的额外解释,让开发人员能够理解您的推理过程,那将非常有用。对于那些对语法不太熟悉或者正在努力理解概念的新手开发人员来说,这尤其有帮助。您是否可以编辑您的答案,以包含额外的细节,以造福社区? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接