'Django channels rest framework don't return data of subscription in production environment (gunicorn/daphne/nginx)
Hi everyone I deployed django with channels via daphne for websocket and gunicorn for normal request http with reverse proxy nginx, I have a problem that has no answer, it works correctly locally. I used the django library djangochannelsrestframework: djangochannelsrestframework to receive variations on ORM models via websocket
I use this version of django,channels.
- django==3.0.8
- djangochannelsrestframework==0.1.0
- channels==2.4.0
- daphne==2.5.0
- gunicorn==20.0.4
consumer.py
import json
from channels.generic.websocket import WebsocketConsumer
from djangochannelsrestframework.observer.generics import ObserverModelInstanceMixin
from app_mobile.API.Client.Utente.serializer import UserSerializer
from app_mobile.API.Client.Azienda.serializer import AziendaObserverSerializer
from app_mobile.API.Client.Ordini.serializer import OrdineConsumerSerializer
from app_mobile.models import Azienda, Ordine, User
from djangochannelsrestframework import permissions
from djangochannelsrestframework.generics import GenericAsyncAPIConsumer
class AziendaObserver(ObserverModelInstanceMixin, GenericAsyncAPIConsumer):
queryset = Azienda.objects.all()
serializer_class = AziendaObserverSerializer
permission_classes = (permissions.AllowAny,)
routing.py
application = ProtocolTypeRouter({
"websocket": AuthMiddlewareStack(
URLRouter([
path("ws/observer/azienda/", AziendaObserver)
]),
),
})
nginx
# Enable upgrading of connection (and websocket proxying) depending on the
# presence of the upgrade field in the client request header
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen xx.it:80;
server_name xx.it www.xx.it;
return 301 https://xx.it$request_uri;
}
server {
listen xx.it:443 ssl http2;
server_name xx.it www.xx.it;
charset utf-8;
access_log /var/log/nginx/xx.it.access.log;
error_log /var/log/nginx/xx.it.com.error.log;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/django/xx/;
expires 1M;
access_log off;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
location / {
proxy_pass http://0.0.0.0:8000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
}
location /ws/ {
proxy_pass http://0.0.0.0:8443;
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_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
ssl_protocols TLSv1.2;
ssl_certificate /etc/letsencrypt/live/xx.it/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/xx.it/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
gunicorn.service
Description=gunicorn
After=network.target
[Service]
PIDFile=/run/gunicorn/pid
User=django
Group=www-data
WorkingDirectory=/home/xxx/xxx
Environment="DJANGO_SETTINGS_MODULE=xxx.settings"
ExecStart=/home/django/xxx/venv/bin/gunicorn xxx.wsgi --bind 0.0.0.0:8000 --log-level error --log-file=- --workers 5 --preload --access-logfile /home/django/xx/logs/gunicorn/output.log --error-logfile /home/django/xx/logs/gunicorn/error.log
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
Restart=on-abort
PrivateTmp=true
#StandardOutput=append:/home/xxx/xxx/logs/gunicorn/output.log
#StandardError=append:/home/xxx/xxx/logs/gunicorn/error.log
[Install]
WantedBy=multi-user.target
daphne.service
[Unit]
Description=daphne daemon
After=network.target
[Service]
PIDFile=/run/daphne/pid
User=django
Group=www-data
WorkingDirectory=/home/django/xxx
Environment="DJANGO_SETTINGS_MODULE=xxx.settings"
ExecStart=/home/django/xxx/venv/bin/daphne --bind 0.0.0.0 --port 8443 --verbosity 3 --access-log /home/django/xx/logs/daphne/access.log xxx.asgi:application
#ExecStart=/home/django/xxx/venv/bin/daphne -e ssl:8443:privateKey=privkey.pem:certKey=fullchain.pem -v 3 --access-log /home/django/xx/logs/daphne/access.log xxx.asgi:application
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
Restart=on-abort
PrivateTmp=true
StandardOutput=append:/home/django/xxx/logs/daphne/access.log
StandardError=append:/home/django/xxx/logs/daphne/error.log
[Install]
WantedBy=multi-user.target
In local the websocket works perfectly, if I subscribe for example to a model with "pk = 1" if it is updated I receive changes in production the websocket connects but then I don't receive anything when I update the signed model.
var ws_scheme = window.location.protocol == "https:" ? "wss" : "ws";
var conn = new ReconnectingWebSocket(ws_scheme + '://'
+ window.location.host
+ '/ws/observer/azienda/?token=9e1058f3d0539b51ff7350ae6f0b096a6553b240', null, {
debug: true,
reconnectInterval: 3000
});
conn.onmessage = function (e) {
console.log("connesso")
console.log(e.data);
};
conn.onopen = () => conn.send(
(JSON.stringify({
"action": "subscribe_instance",
'pk': 1,
"request_id": 34,
})
));
conn.onclose = function (e) {
console.log(e)
console.error('Error websocket');
};
Locally, I receive changes via websocket
connesso
(index):26 {"errors": [], "data": null, "action": "subscribe_instance", "response_status": 201, "request_id": 34}
(index):25 connesso
(index):26 {"errors": [], "data": {"id": 1}, "action": "update", "response_status": 200, "request_id": 34}
In production after the websocket connects I do not receive any updates if I modify for example the company with pk = 1
connesso
(index):26 {"errors": [], "data": null, "action": "subscribe_instance", "response_status": 201, "request_id": 34}
Log daphne prod:
127.0.0.1:55674 - - [31/Jul/2020:11:00:29] "WSCONNECTING /ws/observer/azienda/" - -
127.0.0.1:55674 - - [31/Jul/2020:11:00:29] "WSCONNECT /ws/observer/azienda/" - -
Log daphne local
WebSocket HANDSHAKING /ws/observer/azienda/ [172.27.0.1:60296]
WebSocket CONNECT /ws/observer/azienda/ [172.27.0.1:60296]
Final Solution for work gunicorn/daphne with nginx!
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen xxx.it:443 ssl http2;
server_name xx.it www.xx.it;
tcp_nodelay on;
client_max_body_size 20M;
access_log /var/log/nginx/xx.it.access.log;
error_log /var/log/nginx/xx.it.com.error.log;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/django/xxx/;
expires 1M;
access_log off;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_redirect off;
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_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Connection "";
server_tokens off;
proxy_buffering on;
if (!-f $request_filename) {
proxy_pass http://127.0.0.1:8443;
break;
}
}
location /ws {
proxy_pass http://127.0.0.1:8443;
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_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
ssl_certificate /etc/letsencrypt/live/xxx.it/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/xx.it/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|