django - Nginx/Daphne/WebSocket 连接失败:WebSocket 握手期间出错:意外响应代码:404
问题描述
所以我在AWS EC2 实例上的聊天项目中使用Ubuntu 18.04.3 LTS、Django 3.0.0、Python 3.6、Nginx、Daphne、docker、Channels。我开始了一个类似于Channels教程的项目。我正在尝试通过 wss 协议建立 websocket 连接。当主机是 http 并且 websocket 连接是 ws 协议时,一切都很好。但是如果主机是 https 并且 websocket 连接是 wss 协议,则会出错。错误如下。
(索引):16 WebSocket 连接到“wss://mydomain/ws/chat/1/”失败:WebSocket 握手期间出错:意外响应代码:404
我正在运行Daphne的 django aspi 应用程序。使用Channels-redis作为通道层。并通过docker运行 redis 。这是我运行应用服务器的方式:
daphne -u /home/ubuntu/virtualenvs/src/werewolves/werewolves.sock werewolves.asgi:application
sudo docker run --restart unless-stopped -p 6379:6379 -d redis:2.8
我在 django 项目的 settings.py 中的 channel_layers。
#settings.py
...
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": ["127.0.0.1",6379],
"symmetric_encryption_keys": ["mykey"],
},
}
}
...
这是我的 nginx 设置:
upstream websocket {
server unix:/home/ubuntu/virtualenvs/src/werewolves/werewolves.sock;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name mydomain;
location = /favicon.ico { access_log off; log_not_found off; }
location ^~ /static/ {
autoindex on;
alias /home/ubuntu/virtualenvs/static/static-only/; #STATIC_ROOT
}
# SSL settings
ssl on;
listen 443 ssl http2;
listen [::]:443 ssl http2 default_server;
ssl_certificate /etc/letsencrypt/live/mydomain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mydomain/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/mydomain/fullchain.pem;
ssl_session_timeout 10m;
ssl_session cache shared:SSL:1m;
location / {
# proxy setting for django using wsgi
include proxy_params;
#proxy_pass 0.0.0.0:8000;
proxy_pass websocket;
# CORS config. settings for using AWS S3 serving static file
set $origin '*'; #origin url;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# Custom headers and headers various browsers *should* be OK
# with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Co$
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' $origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Co$
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' $origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Co$
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
}
location ^~ /ws/ {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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-Frame-Options SAMEORIGIN;
proxy_pass http://websocket;
}
location ~* \.(js|css)$ {
expires -1;
}
}
这是我的routing.py。
# mysite/routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import gameroom.routing
application = ProtocolTypeRouter({
# (http->django views is added by default)
'websocket': AuthMiddlewareStack(
URLRouter(
gameroom.routing.websocket_urlpatterns
)
),
})
# chat/routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'^ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer),
]
这就是我运行我的应用程序的方式。
此 JS websocket 命令适用于 URL:http://mydomain/chat/1/
var chatSocket = new WebSocket('ws://' + window.location.host + '/ws/chat/1/');
问题是这个 JS websocket 命令不适用于 URL:https://mydomain/chat/1/
var chatSocket = new WebSocket('wss://' + window.location.host + '/ws/chat/1/');
来自浏览器的错误消息是:
(索引):16 WebSocket 连接到“wss://mydomain/ws/chat/1/”失败:WebSocket 握手期间出错:意外响应代码:404
达芙妮返回以下消息。
None - - [23/Dec/2019:10:07:05] "GET /chat/1/" 200 1413
Not Found: /ws/chat/1/
2019-12-23 10:07:06,504 WARNING Not Found: /ws/chat/1/
None - - [23/Dec/2019:10:07:06] "GET /ws/chat/1/" 404 2083
我应该如何修改我的 Nginx 设置?顺便说一句,我的 AWS EC2 实例没有 ELB(负载均衡器)。
解决方案
nginx的例子:
upstream channels-backend {
server 0.0.0.0:8099;
}
server {
listen 80;
server_name {domain};
return 301 https://$host$request_uri;
}
server {
proxy_connect_timeout 220s;
proxy_read_timeout 220s;
client_max_body_size 4G;
listen 443 ssl;
server_name {domain};
ssl_certificate {cert-path};
ssl_certificate_key {cert-path};
access_log /{log-path};
error_log /{log-path};
location /sockets {
try_files $uri @proxy_to_app;
}
location @proxy_to_app {
proxy_pass http://channels-backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $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;
}
}
达芬:
daphne -b 0.0.0.0 -p 8099 {your-app}.asgi:application
推荐阅读
- python - 神经网络风格GAN风格混合麻烦
- r - 使用 r 自动选择 .csv 文件中的行和列
- c++ - 如何使用包装器从 Swift 调用 C++ 函数并使用终端进行编译?
- macos - Apple Photos:以正确的方式从 photos.db 中读取面孔/人物
- java - 无法使用休眠将数据库保存到 h2 数据库
- java - 如何在不使用日历的情况下使用日期添加天数
- typescript - 使用字符串键和数字键在打字稿中重载索引器
- android - 哪些广告网络在向用户支付真钱时允许奖励视频?
- python - 在 Python 中替换文件中的特定字符串
- pyspark - 数字海洋中的火花集群设置